mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 17:24:23 +00:00
; Merge from https://github.com/Ravenbrook/mps
This commit is contained in:
commit
026d2ff9c1
1094 changed files with 245719 additions and 0 deletions
114
mps/.github/workflows/build-and-test.yml
vendored
Normal file
114
mps/.github/workflows/build-and-test.yml
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# .github/workflows/build-and-test.yml -- GitHub CI build and test configuration for the MPS
|
||||
#
|
||||
# Copyright (c) 2019-2022 `GitHub contributors`_ (MIT License).
|
||||
# Copyright (c) 2023 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# See design.mps.test.ci.
|
||||
#
|
||||
# TODO: Exclude certain branches.
|
||||
#
|
||||
# TODO: Regular builds of version branches. See
|
||||
# <https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onschedule>.
|
||||
|
||||
name: build and test
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
# Also run when triggered manually, e.g. by tool/github-ci-kick
|
||||
# <https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow>
|
||||
- workflow_dispatch
|
||||
|
||||
jobs:
|
||||
|
||||
posix:
|
||||
|
||||
# The build matrix for GitHub CI on Posix platforms
|
||||
#
|
||||
# See design.mps.tests.ci.github.platforms.
|
||||
#
|
||||
# FreeBSD and ARM64 targets are in Travis CI, configured by
|
||||
# .travis.yml.
|
||||
#
|
||||
# See <https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#using-a-matrix-strategy>.
|
||||
|
||||
strategy:
|
||||
fail-fast: false # don't cancel all builds when one build fails
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
compiler: [clang, gcc]
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
compiler: gcc
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
# See design.mps.tests.ci.run.posix.
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # see <https://github.com/actions/checkout/blob/main/README.md#usage>
|
||||
- run: CC=${{ matrix.compiler }} ./configure
|
||||
- run: make
|
||||
- run: make test
|
||||
|
||||
windows:
|
||||
|
||||
runs-on: windows-latest
|
||||
|
||||
# See design.mps.tests.ci.run.windows.
|
||||
#
|
||||
# The path to Visual Studio is documented at
|
||||
# <https://github.com/actions/runner-images/blob/e6fcf60b8e6c0f80a065327eaefe836881c28b68/images/win/Windows2022-Readme.md?plain=1#L215>.
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # see <https://github.com/actions/checkout/blob/main/README.md#usage>
|
||||
- run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
cd code
|
||||
nmake /f w3i6mv.nmk all testci testansi testpollnone
|
||||
shell: cmd
|
||||
|
||||
|
||||
# A. REFERENCES
|
||||
#
|
||||
# [GitHub CI] "About continuous integration"; <https://docs.github.com/en/actions/automating-builds-and-tests/about-continuous-integration>.
|
||||
#
|
||||
#
|
||||
# B. DOCUMENT HISTORY
|
||||
#
|
||||
# 2023-01-11 RB Adapted from <https://github.com/actions/starter-workflows/blob/9f245d9aba830ad16a097a45c78331a05114d815/ci/c-cpp.yml>.
|
||||
# 2023-01-15 RB Added licence and document history.
|
||||
#
|
||||
#
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# NOTE: This is the `MIT Licence <https://opensource.org/licenses/MIT>`_
|
||||
# inherited from
|
||||
# <https://github.com/actions/starter-workflows/blob/9f245d9aba830ad16a097a45c78331a05114d815/ci/c-cpp.yml> and not the usual licence for the MPS.
|
||||
#
|
||||
# Copyright (c) 2019-2022 `GitHub contributors`_.
|
||||
# Copyright (c) 2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the "Software"), to deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# .. _GitHub contributors: https://github.com/actions/starter-workflows/commits/1d9d6d7fb0a8a27ef98efbbfa9689cd14c906383/ci/c-cpp.yml
|
||||
#
|
||||
#
|
||||
# $Id$
|
||||
21
mps/.github/workflows/fixme-check.yml
vendored
Normal file
21
mps/.github/workflows/fixme-check.yml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# .github/workflows/fixme-check.yml -- check for FIXME task labels
|
||||
#
|
||||
# This is a GitHub CI workflow
|
||||
# <https://docs.github.com/en/actions/automating-builds-and-tests/about-continuous-integration>
|
||||
# to check for FIXME and similar task labels left unresolved in the
|
||||
# MPS source tree.
|
||||
|
||||
name: FIXME check
|
||||
|
||||
on:
|
||||
# Run as part of CI checks on branch push and on merged pull request.
|
||||
- push
|
||||
- pull_request
|
||||
- workflow_dispatch # allow manual triggering
|
||||
|
||||
jobs:
|
||||
check-fixme:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # see <https://github.com/actions/checkout/blob/main/README.md#usage>
|
||||
- run: tool/check-fixme
|
||||
23
mps/.github/workflows/rst-check.yml
vendored
Normal file
23
mps/.github/workflows/rst-check.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# .github/workflows/rst-check.yml -- check syntax of reStructuredText files
|
||||
#
|
||||
# This is a GitHub CI workflow
|
||||
# <https://docs.github.com/en/actions/using-workflows/about-workflows>
|
||||
# to check the syntax of reStructuredText files.
|
||||
|
||||
name: reStructuredText syntax check
|
||||
|
||||
on:
|
||||
# Run as part of CI checks on branch push and on merged pull request.
|
||||
- push
|
||||
- pull_request
|
||||
- workflow_dispatch # allow manual triggering
|
||||
|
||||
jobs:
|
||||
check-rst:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # see <https://github.com/actions/checkout/blob/main/README.md#usage>
|
||||
- name: Install docutils
|
||||
run: sudo apt-get install -y docutils
|
||||
- name: Check reStructuredText syntax
|
||||
run: tool/check-rst
|
||||
23
mps/.github/workflows/shell-script-check.yml
vendored
Normal file
23
mps/.github/workflows/shell-script-check.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# .github/workflows/shell-script-check.yml -- check shell scripts
|
||||
#
|
||||
# This is a GitHub CI workflow
|
||||
# <https://docs.github.com/en/actions/using-workflows/about-workflows>
|
||||
# to check shell scripts.
|
||||
|
||||
name: shell script check
|
||||
|
||||
on:
|
||||
# Run as part of CI checks on branch push and on merged pull request.
|
||||
- push
|
||||
- pull_request
|
||||
- workflow_dispatch # allow manual triggering
|
||||
|
||||
jobs:
|
||||
check-shell-scripts:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # see <https://github.com/actions/checkout/blob/main/README.md#usage>
|
||||
- name: Install shellcheck
|
||||
run: sudo apt-get install -y shellcheck
|
||||
- name: Check shell scripts
|
||||
run: tool/check-shell-scripts
|
||||
32
mps/.gitignore
vendored
Normal file
32
mps/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# .p4ignore -- Perforce files to ignore from MPS Project
|
||||
# $Id$
|
||||
# Don't forget to consider files in more specific directories.
|
||||
# Personal Perforce configurations
|
||||
.p4config
|
||||
# Mac OS X Finder turds
|
||||
.DS_Store
|
||||
# Patch results
|
||||
*.orig
|
||||
*.rej
|
||||
# Autoconf and Automake output
|
||||
Makefile
|
||||
autom4te.cache
|
||||
config.log
|
||||
config.status
|
||||
.deps
|
||||
.dirstamp
|
||||
bin
|
||||
lib
|
||||
# Misc
|
||||
TAGS
|
||||
*.dSYM
|
||||
*.pyc
|
||||
test/obj
|
||||
test/test/log
|
||||
test/test/obj
|
||||
....gcda
|
||||
....gcno
|
||||
\#*#
|
||||
*~
|
||||
.#.*
|
||||
core
|
||||
1
mps/.p4ignore
Symbolic link
1
mps/.p4ignore
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
.gitignore
|
||||
70
mps/.readthedocs.yaml
Normal file
70
mps/.readthedocs.yaml
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# .readthedocs.yaml -- Build configuration for MPS manual on Read The Docs
|
||||
#
|
||||
# Copyright (c) 2023 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# This file controls how Read the Docs builds and publishes the MPS
|
||||
# manual at <https://memory-pool-system.readthedocs.io/>.
|
||||
#
|
||||
# See <https://docs.readthedocs.io/en/stable/config-file/v2.html> for
|
||||
# the file format.
|
||||
#
|
||||
# Project configuration is at
|
||||
# <https://readthedocs.org/projects/memory-pool-system/>. The GitHub
|
||||
# Ravenbot user <sysadmins@ravenbrook.com> is an administrator
|
||||
# <https://readthedocs.org/profiles/Ravenbot/>.
|
||||
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: '3'
|
||||
|
||||
python:
|
||||
install:
|
||||
- requirements: manual/requirements.pip
|
||||
|
||||
sphinx:
|
||||
configuration: manual/source/conf.py
|
||||
fail_on_warning: true
|
||||
|
||||
# A. REFERENCES
|
||||
#
|
||||
# [Readthedocs] "Read the Docs: Documentation Simplified";
|
||||
# <https://docs.readthedocs.io/en/stable/>.
|
||||
#
|
||||
#
|
||||
# B. DOCUMENT HISTORY
|
||||
#
|
||||
# 2023-02-02 RB Created as part of MPS GitHub migration.
|
||||
#
|
||||
#
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright © 2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
#
|
||||
# $Id$
|
||||
105
mps/.travis.yml
Normal file
105
mps/.travis.yml
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# .travis.yml -- Travis CI configuration for the MPS
|
||||
#
|
||||
# Copyright (c) 2013-2023 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# See design.mps.test.ci.
|
||||
|
||||
# Some branches don't need builds. Add them here to avoid using build
|
||||
# resources and unnecessary build messages.
|
||||
branches:
|
||||
except:
|
||||
- branch/2023-01-07/pull-request-merge-procedure
|
||||
- branch/2023-01-11/github-ci
|
||||
|
||||
language: c # see <https://docs.travis-ci.com/user/languages/c/>.
|
||||
|
||||
# The build matrix for Travis CI
|
||||
#
|
||||
# See design.mps.tests.ci.travis.platforms.
|
||||
#
|
||||
# Most x86_64/amd64 builds are in GitHub CI, configured by
|
||||
# .github/workflows/build-and-test.yml.
|
||||
|
||||
os:
|
||||
- freebsd
|
||||
- linux
|
||||
arch:
|
||||
- arm64
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
script: # see design.mps.test.ci.run.posix
|
||||
- ./configure --prefix=$PWD/prefix && make install && make test
|
||||
|
||||
matrix:
|
||||
|
||||
# Extra build jobs to add to the matrix
|
||||
include:
|
||||
|
||||
# GitHub CI does not provide FreeBSD
|
||||
# <https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources>
|
||||
# on any architecture, so we add it here for amd64. See also
|
||||
# design.mps.tests.ci.travis.platforms.
|
||||
|
||||
- os: freebsd
|
||||
arch: amd64
|
||||
compiler: clang
|
||||
- os: freebsd
|
||||
arch: amd64
|
||||
compiler: gcc
|
||||
|
||||
# Specific combinations to exclude from the matrix
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- mps-travis@ravenbrook.com
|
||||
|
||||
# This shows how you can ask Travis to install or update packages.
|
||||
#before_install:
|
||||
# - if test "$TRAVIS_OS_NAME" = "linux"; then sudo apt-get -qq update; fi
|
||||
# - if test "$TRAVIS_OS_NAME" = "linux"; then sudo apt-get install -y gcc-4.7; fi
|
||||
|
||||
|
||||
# A. REFERENCES
|
||||
#
|
||||
#
|
||||
# B. DOCUMENT HISTORY
|
||||
#
|
||||
# 2013-05-19 RB Created.
|
||||
# 2023-01-15 RB Added licence and (note) document history.
|
||||
#
|
||||
#
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2013-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
#
|
||||
# $Id$
|
||||
1
mps/CONTRIBUTING
Symbolic link
1
mps/CONTRIBUTING
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
contributing.rst
|
||||
1
mps/INSTALL
Symbolic link
1
mps/INSTALL
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
manual/build.txt
|
||||
115
mps/Makefile.in
Normal file
115
mps/Makefile.in
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Makefile.in -- source for autoconf Makefile
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (C) 2012-2023 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# YOU DON'T NEED AUTOCONF TO BUILD THE MPS
|
||||
# This is just here for people who want or expect a configure script.
|
||||
# See [Building the Memory Pool System](manual/build.txt) for how best
|
||||
# to build and integrate the MPS.
|
||||
#
|
||||
# THIS IS NOT A GNU MAKEFILE
|
||||
# This makefile be compatible with the default make on every (Posix)
|
||||
# target platform, e.g. BSD make.
|
||||
#
|
||||
|
||||
INSTALL=@INSTALL@
|
||||
INSTALL_DATA=@INSTALL_DATA@
|
||||
INSTALL_PROGRAM=@INSTALL_PROGRAM@
|
||||
MAKE=@MAKE@
|
||||
MPS_OS_NAME=@MPS_OS_NAME@
|
||||
MPS_ARCH_NAME=@MPS_ARCH_NAME@
|
||||
MPS_BUILD_NAME=@MPS_BUILD_NAME@
|
||||
MPS_TARGET_NAME=$(MPS_OS_NAME)$(MPS_ARCH_NAME)$(MPS_BUILD_NAME)
|
||||
EXTRA_TARGETS=@EXTRA_TARGETS@
|
||||
prefix=$(DESTDIR)@prefix@
|
||||
TARGET_OPTS=-C code -f $(MPS_TARGET_NAME).gmk EXTRA_TARGETS="$(EXTRA_TARGETS)"
|
||||
XCODEBUILD=xcrun xcodebuild -project code/mps.xcodeproj
|
||||
|
||||
all: @BUILD_TARGET@
|
||||
|
||||
build-via-make:
|
||||
$(MAKE) $(TARGET_OPTS)
|
||||
|
||||
clean-make-build:
|
||||
$(MAKE) $(TARGET_OPTS) clean
|
||||
|
||||
install-make-build: make-install-dirs build-via-make
|
||||
$(INSTALL_DATA) code/mps*.h $(prefix)/include/
|
||||
$(INSTALL_DATA) code/$(MPS_TARGET_NAME)/cool/mps.a $(prefix)/lib/libmps-debug.a
|
||||
$(INSTALL_DATA) code/$(MPS_TARGET_NAME)/hot/mps.a $(prefix)/lib/libmps.a
|
||||
for PROGRAM in $(EXTRA_TARGETS); do $(INSTALL_PROGRAM) code/$(MPS_TARGET_NAME)/hot/$$PROGRAM $(prefix)/bin/$$PROGRAM; done
|
||||
|
||||
build-via-xcode:
|
||||
$(XCODEBUILD) -config Debug
|
||||
$(XCODEBUILD) -config Release
|
||||
|
||||
clean-xcode-build:
|
||||
$(XCODEBUILD) -config Debug clean
|
||||
$(XCODEBUILD) -config Release clean
|
||||
|
||||
install-xcode-build: make-install-dirs build-via-xcode
|
||||
$(INSTALL_DATA) code/mps*.h $(prefix)/include/
|
||||
$(INSTALL_DATA) code/xc/Debug/libmps.a $(prefix)/lib/libmps-debug.a
|
||||
$(INSTALL_DATA) code/xc/Release/libmps.a $(prefix)/lib/libmps.a
|
||||
for PROGRAM in $(EXTRA_TARGETS); do $(INSTALL_PROGRAM) code/xc/Release/$$PROGRAM $(prefix)/bin/$$PROGRAM; done
|
||||
|
||||
Makefile: Makefile.in config.status
|
||||
./config.status Makefile
|
||||
|
||||
clean: @CLEAN_TARGET@
|
||||
|
||||
config.status: configure
|
||||
|
||||
configure: configure.ac
|
||||
autoreconf -vif
|
||||
|
||||
distclean: clean
|
||||
rm -rf autom4te.cache/ config.log config.status Makefile
|
||||
|
||||
make-install-dirs:
|
||||
mkdir -p $(prefix)/bin
|
||||
mkdir -p $(prefix)/lib
|
||||
mkdir -p $(prefix)/include
|
||||
|
||||
install: @INSTALL_TARGET@
|
||||
|
||||
test-make-build:
|
||||
$(MAKE) $(TARGET_OPTS) testci testratio testscheme
|
||||
$(MAKE) -C code -f anan$(MPS_BUILD_NAME).gmk VARIETY=cool clean testansi
|
||||
$(MAKE) -C code -f anan$(MPS_BUILD_NAME).gmk VARIETY=cool CFLAGS="-DCONFIG_POLL_NONE" clean testpollnone
|
||||
|
||||
test-xcode-build:
|
||||
$(XCODEBUILD) -config Debug -target testci
|
||||
$(XCODEBUILD) -config Release -target testci
|
||||
|
||||
test: @TEST_TARGET@
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2012-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1
mps/NEWS
Symbolic link
1
mps/NEWS
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
manual/source/release.rst
|
||||
1
mps/README
Symbolic link
1
mps/README
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
readme.txt
|
||||
63
mps/code/.gitignore
vendored
Normal file
63
mps/code/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# code/.p4ignore -- Perforce files to ignore list
|
||||
# $Id$
|
||||
# Make output
|
||||
anangc
|
||||
ananll
|
||||
ananmv
|
||||
fri3gc
|
||||
fri3ll
|
||||
fri6gc
|
||||
fri6ll
|
||||
lia6gc
|
||||
lia6ll
|
||||
lii3gc
|
||||
lii6gc
|
||||
lii6ll
|
||||
w3i3mv
|
||||
w3i6mv
|
||||
xca6ll
|
||||
xci3gc
|
||||
xci3ll
|
||||
xci6gc
|
||||
xci6ll
|
||||
# Visual Studio junk
|
||||
Debug
|
||||
Release
|
||||
*.filters
|
||||
*.user
|
||||
*.suo
|
||||
# Telemetry event logs
|
||||
mpsio*.log
|
||||
mpsio*.sql
|
||||
mpsio*.txt
|
||||
# Build products
|
||||
*.o
|
||||
*.obj
|
||||
*.a
|
||||
*.so
|
||||
*.lib
|
||||
*.exe
|
||||
a.out
|
||||
core
|
||||
# Xcode junk
|
||||
xc
|
||||
mps.xcodeproj/xcuserdata
|
||||
mps.xcodeproj/project.xcworkspace
|
||||
tags
|
||||
# Temporary files
|
||||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
# Mac OS X Finder turds
|
||||
.DS_Store
|
||||
# Emacs backups
|
||||
*~
|
||||
# GNU make dependencies
|
||||
*/*/*.d
|
||||
1
mps/code/.p4ignore
Symbolic link
1
mps/code/.p4ignore
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
.gitignore
|
||||
330
mps/code/abq.c
Normal file
330
mps/code/abq.c
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
/* abq.c: QUEUE IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .purpose: A fixed-length FIFO queue.
|
||||
*
|
||||
* .design: <design/abq>
|
||||
*/
|
||||
|
||||
#include "meter.h"
|
||||
#include "abq.h"
|
||||
#include "mpm.h"
|
||||
|
||||
SRCID(abq, "$Id$");
|
||||
|
||||
|
||||
/* Private prototypes */
|
||||
|
||||
static Size ABQQueueSize(Count elements, Size elementSize);
|
||||
static Index ABQNextIndex(ABQ abq, Index index);
|
||||
static void *ABQElement(ABQ abq, Index index);
|
||||
|
||||
|
||||
/* Methods */
|
||||
|
||||
/* ABQInit -- Initialize an ABQ
|
||||
*
|
||||
* elements is the number of elements the queue can hold
|
||||
*/
|
||||
Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize)
|
||||
{
|
||||
void *p;
|
||||
Res res;
|
||||
|
||||
AVERT(Arena, arena);
|
||||
AVER(abq != NULL);
|
||||
AVER(elements > 0);
|
||||
|
||||
/* Allocate a dummy extra element in order to be able to distinguish
|
||||
"empty" from "full" */
|
||||
elements = elements + 1;
|
||||
|
||||
res = ControlAlloc(&p, arena, ABQQueueSize(elements, elementSize));
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
abq->elements = elements;
|
||||
abq->elementSize = elementSize;
|
||||
abq->in = 0;
|
||||
abq->out = 0;
|
||||
abq->queue = p;
|
||||
|
||||
METER_INIT(abq->push, "push", owner);
|
||||
METER_INIT(abq->pop, "pop", owner);
|
||||
METER_INIT(abq->peek, "peek", owner);
|
||||
METER_INIT(abq->delete, "delete", owner);
|
||||
|
||||
abq->sig = ABQSig;
|
||||
|
||||
AVERT(ABQ, abq);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* ABQCheck -- validate an ABQ */
|
||||
Bool ABQCheck(ABQ abq)
|
||||
{
|
||||
CHECKS(ABQ, abq);
|
||||
CHECKL(abq->elements > 0);
|
||||
CHECKL(abq->elementSize > 0);
|
||||
CHECKL(abq->in < abq->elements);
|
||||
CHECKL(abq->out < abq->elements);
|
||||
CHECKL(abq->queue != NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ABQFinish -- finish an ABQ */
|
||||
void ABQFinish(Arena arena, ABQ abq)
|
||||
{
|
||||
AVERT(Arena, arena);
|
||||
AVERT(ABQ, abq);
|
||||
|
||||
METER_EMIT(&abq->push);
|
||||
METER_EMIT(&abq->pop);
|
||||
METER_EMIT(&abq->peek);
|
||||
METER_EMIT(&abq->delete);
|
||||
ControlFree(arena, abq->queue, ABQQueueSize(abq->elements, abq->elementSize));
|
||||
|
||||
abq->elements = 0;
|
||||
abq->queue = NULL;
|
||||
|
||||
abq->sig = SigInvalid;
|
||||
}
|
||||
|
||||
|
||||
/* ABQPush -- push an element onto the tail of the ABQ */
|
||||
Bool ABQPush(ABQ abq, void *element)
|
||||
{
|
||||
AVERT(ABQ, abq);
|
||||
|
||||
METER_ACC(abq->push, ABQDepth(abq));
|
||||
|
||||
if (ABQIsFull(abq))
|
||||
return FALSE;
|
||||
|
||||
(void)mps_lib_memcpy(ABQElement(abq, abq->in), element, abq->elementSize);
|
||||
abq->in = ABQNextIndex(abq, abq->in);
|
||||
|
||||
AVERT(ABQ, abq);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ABQPop -- pop an element from the head of the ABQ */
|
||||
Bool ABQPop(ABQ abq, void *elementReturn)
|
||||
{
|
||||
AVER(elementReturn != NULL);
|
||||
AVERT(ABQ, abq);
|
||||
|
||||
METER_ACC(abq->pop, ABQDepth(abq));
|
||||
|
||||
if (ABQIsEmpty(abq))
|
||||
return FALSE;
|
||||
|
||||
(void)mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize);
|
||||
|
||||
abq->out = ABQNextIndex(abq, abq->out);
|
||||
|
||||
AVERT(ABQ, abq);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ABQPeek -- peek at the head of the ABQ */
|
||||
Bool ABQPeek(ABQ abq, void *elementReturn)
|
||||
{
|
||||
AVER(elementReturn != NULL);
|
||||
AVERT(ABQ, abq);
|
||||
|
||||
METER_ACC(abq->peek, ABQDepth(abq));
|
||||
|
||||
if (ABQIsEmpty(abq))
|
||||
return FALSE;
|
||||
|
||||
(void)mps_lib_memcpy(elementReturn, ABQElement(abq, abq->out), abq->elementSize);
|
||||
|
||||
/* Identical to pop, but don't increment out */
|
||||
|
||||
AVERT(ABQ, abq);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ABQDescribe -- Describe an ABQ */
|
||||
Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
Res res;
|
||||
Index index;
|
||||
|
||||
if (!TESTT(ABQ, abq))
|
||||
return ResFAIL;
|
||||
if (stream == NULL)
|
||||
return ResFAIL;
|
||||
|
||||
res = WriteF(stream, depth,
|
||||
"ABQ $P {\n", (WriteFP)abq,
|
||||
" elements $U\n", (WriteFU)abq->elements,
|
||||
" elementSize $W\n", (WriteFW)abq->elementSize,
|
||||
" in $U\n", (WriteFU)abq->in,
|
||||
" out $U\n", (WriteFU)abq->out,
|
||||
" queue:\n",
|
||||
NULL);
|
||||
if(res != ResOK)
|
||||
return res;
|
||||
|
||||
for (index = abq->out; index != abq->in; ) {
|
||||
res = (*describeElement)(ABQElement(abq, index), stream, depth + 2);
|
||||
if(res != ResOK)
|
||||
return res;
|
||||
index = ABQNextIndex(abq, index);
|
||||
}
|
||||
|
||||
METER_WRITE(abq->push, stream, depth + 2);
|
||||
METER_WRITE(abq->pop, stream, depth + 2);
|
||||
METER_WRITE(abq->peek, stream, depth + 2);
|
||||
METER_WRITE(abq->delete, stream, depth + 2);
|
||||
|
||||
res = WriteF(stream, depth, "} ABQ $P\n", (WriteFP)abq, NULL);
|
||||
if(res != ResOK)
|
||||
return res;
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* ABQIsEmpty -- Is an ABQ empty? */
|
||||
Bool ABQIsEmpty(ABQ abq)
|
||||
{
|
||||
AVERT(ABQ, abq);
|
||||
|
||||
return abq->out == abq->in;
|
||||
}
|
||||
|
||||
|
||||
/* ABQIsFull -- Is an ABQ full? */
|
||||
Bool ABQIsFull(ABQ abq)
|
||||
{
|
||||
AVERT(ABQ, abq);
|
||||
|
||||
return ABQNextIndex(abq, abq->in) == abq->out;
|
||||
}
|
||||
|
||||
|
||||
/* ABQDepth -- return the number of elements in an ABQ */
|
||||
Count ABQDepth(ABQ abq)
|
||||
{
|
||||
Index out, in;
|
||||
|
||||
AVERT(ABQ, abq);
|
||||
out = abq->out;
|
||||
in = abq->in;
|
||||
|
||||
if (in >= out)
|
||||
return in - out;
|
||||
else
|
||||
return in + abq->elements - out;
|
||||
}
|
||||
|
||||
|
||||
/* ABQIterate -- call 'visitor' for each element in an ABQ */
|
||||
void ABQIterate(ABQ abq, ABQVisitor visitor, void *closure)
|
||||
{
|
||||
Index copy, index, in;
|
||||
|
||||
AVERT(ABQ, abq);
|
||||
AVER(FUNCHECK(visitor));
|
||||
|
||||
copy = abq->out;
|
||||
index = abq->out;
|
||||
in = abq->in;
|
||||
|
||||
while (index != in) {
|
||||
void *element = ABQElement(abq, index);
|
||||
Bool delete = FALSE;
|
||||
Bool cont;
|
||||
cont = (*visitor)(&delete, element, closure);
|
||||
AVERT(Bool, cont);
|
||||
AVERT(Bool, delete);
|
||||
if (!delete) {
|
||||
if (copy != index)
|
||||
(void)mps_lib_memcpy(ABQElement(abq, copy), element, abq->elementSize);
|
||||
copy = ABQNextIndex(abq, copy);
|
||||
}
|
||||
index = ABQNextIndex(abq, index);
|
||||
if (!cont)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If any elements were deleted, need to copy remainder of queue. */
|
||||
if (copy != index) {
|
||||
while (index != in) {
|
||||
(void)mps_lib_memcpy(ABQElement(abq, copy), ABQElement(abq, index),
|
||||
abq->elementSize);
|
||||
copy = ABQNextIndex(abq, copy);
|
||||
index = ABQNextIndex(abq, index);
|
||||
}
|
||||
abq->in = copy;
|
||||
}
|
||||
|
||||
AVERT(ABQ, abq);
|
||||
}
|
||||
|
||||
|
||||
/* ABQQueueSize -- calculate the storage required for the vector to
|
||||
store the elements */
|
||||
static Size ABQQueueSize(Count elements, Size elementSize)
|
||||
{
|
||||
return (Size)(elements * elementSize);
|
||||
}
|
||||
|
||||
|
||||
/* ABQNextIndex -- calculate the next index into the queue vector from
|
||||
the current one */
|
||||
static Index ABQNextIndex(ABQ abq, Index index)
|
||||
{
|
||||
Index next = index + 1;
|
||||
if (next == abq->elements)
|
||||
next = 0;
|
||||
return next;
|
||||
}
|
||||
|
||||
/* ABQElement -- return pointer to the index'th element in the queue
|
||||
vector. */
|
||||
static void *ABQElement(ABQ abq, Index index)
|
||||
{
|
||||
return PointerAdd(abq->queue, index * abq->elementSize);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
90
mps/code/abq.h
Normal file
90
mps/code/abq.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* abq.h: QUEUE INTERFACE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .purpose: A fixed-length FIFO queue.
|
||||
*
|
||||
* .design: <design/abq>
|
||||
*/
|
||||
|
||||
#ifndef abq_h
|
||||
#define abq_h
|
||||
|
||||
#include "meter.h"
|
||||
#include "mpm.h"
|
||||
|
||||
|
||||
/* Signatures */
|
||||
|
||||
#define ABQSig ((Sig)0x519AB099) /* SIGnature ABQ */
|
||||
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
typedef struct ABQStruct *ABQ;
|
||||
typedef Res (*ABQDescribeElement)(void *element, mps_lib_FILE *stream, Count depth);
|
||||
typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closure);
|
||||
|
||||
extern Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize);
|
||||
extern Bool ABQCheck(ABQ abq);
|
||||
extern void ABQFinish(Arena arena, ABQ abq);
|
||||
extern Bool ABQPush(ABQ abq, void *element);
|
||||
extern Bool ABQPop(ABQ abq, void *elementReturn);
|
||||
extern Bool ABQPeek(ABQ abq, void *elementReturn);
|
||||
extern Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *stream, Count depth);
|
||||
extern Bool ABQIsEmpty(ABQ abq);
|
||||
extern Bool ABQIsFull(ABQ abq);
|
||||
extern Count ABQDepth(ABQ abq);
|
||||
extern void ABQIterate(ABQ abq, ABQVisitor visitor, void *closure);
|
||||
|
||||
|
||||
/* Types */
|
||||
|
||||
typedef struct ABQStruct
|
||||
{
|
||||
Sig sig; /* design.mps.sig.field */
|
||||
Count elements;
|
||||
Size elementSize;
|
||||
Index in;
|
||||
Index out;
|
||||
void *queue;
|
||||
|
||||
/* Meter queue depth at each operation */
|
||||
METER_DECL(push)
|
||||
METER_DECL(pop)
|
||||
METER_DECL(peek)
|
||||
METER_DECL(delete)
|
||||
} ABQStruct;
|
||||
|
||||
#endif /* abq_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
210
mps/code/abqtest.c
Normal file
210
mps/code/abqtest.c
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
/* abqtest.c: AVAILABLE BLOCK QUEUE TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*/
|
||||
|
||||
#include "abq.h"
|
||||
#include "mps.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscmfs.h"
|
||||
#include "mpstd.h"
|
||||
#include "testlib.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
|
||||
|
||||
SRCID(abqtest, "$Id$");
|
||||
|
||||
static mps_pool_t pool;
|
||||
static ABQStruct abq; /* the ABQ which we will use */
|
||||
static Size abqSize; /* the size of the current ABQ */
|
||||
|
||||
#define ABQ_SIZE 10
|
||||
#define TEST_ITER 10000
|
||||
|
||||
|
||||
static unsigned long abqRnd(unsigned long n)
|
||||
{
|
||||
return rnd()%n;
|
||||
}
|
||||
|
||||
|
||||
static unsigned pushee = 1;
|
||||
static unsigned popee = 1;
|
||||
static unsigned deleted = 0;
|
||||
|
||||
|
||||
typedef struct TestBlockStruct *TestBlock;
|
||||
|
||||
typedef struct TestBlockStruct
|
||||
{
|
||||
TestBlock next;
|
||||
unsigned id;
|
||||
Addr base;
|
||||
Addr limit;
|
||||
} TestBlockStruct;
|
||||
|
||||
|
||||
static TestBlock testBlocks = NULL;
|
||||
|
||||
|
||||
static TestBlock CreateTestBlock(unsigned no)
|
||||
{
|
||||
TestBlock b;
|
||||
mps_addr_t p;
|
||||
|
||||
die(mps_alloc(&p, pool, sizeof(TestBlockStruct)), "alloc");
|
||||
|
||||
b = p;
|
||||
b->next = testBlocks;
|
||||
b->id = no;
|
||||
b->base = 0;
|
||||
b->limit = 0;
|
||||
|
||||
testBlocks = b;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
static void DestroyTestBlock(TestBlock b)
|
||||
{
|
||||
if (b == testBlocks)
|
||||
testBlocks = b->next;
|
||||
else {
|
||||
TestBlock prev;
|
||||
|
||||
for (prev = testBlocks; prev != 0; prev = prev->next)
|
||||
if (prev->next == b) {
|
||||
prev->next = b->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mps_free(pool, b, sizeof(TestBlockStruct));
|
||||
}
|
||||
|
||||
typedef struct TestClosureStruct *TestClosure;
|
||||
typedef struct TestClosureStruct {
|
||||
TestBlock b;
|
||||
Res res;
|
||||
} TestClosureStruct;
|
||||
|
||||
static Bool TestDeleteCallback(Bool *deleteReturn, void *element,
|
||||
void *closure)
|
||||
{
|
||||
TestBlock *a = (TestBlock *)element;
|
||||
TestClosure cl = (TestClosure)closure;
|
||||
if (*a == cl->b) {
|
||||
*deleteReturn = TRUE;
|
||||
cl->res = ResOK;
|
||||
} else {
|
||||
*deleteReturn = FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void step(void)
|
||||
{
|
||||
TestBlock a;
|
||||
|
||||
switch (abqRnd(9)) {
|
||||
case 0: case 1: case 2: case 3:
|
||||
push:
|
||||
a = CreateTestBlock(pushee);
|
||||
if (!ABQPush(&abq, &a)) {
|
||||
goto pop;
|
||||
}
|
||||
pushee++;
|
||||
break;
|
||||
case 5: case 6: case 7: case 8:
|
||||
pop:
|
||||
if (!ABQPop(&abq, &a)) {
|
||||
goto push;
|
||||
}
|
||||
if (popee == deleted) {
|
||||
popee++;
|
||||
deleted = 0;
|
||||
}
|
||||
cdie(a->id == popee, "pop");
|
||||
popee++;
|
||||
DestroyTestBlock(a);
|
||||
break;
|
||||
default:
|
||||
if (!deleted && (pushee > popee)) {
|
||||
TestBlock b;
|
||||
TestClosureStruct cl;
|
||||
deleted = (unsigned)abqRnd (pushee - popee) + popee;
|
||||
for (b = testBlocks; b != NULL; b = b->next)
|
||||
if (b->id == deleted)
|
||||
break;
|
||||
cdie(b != NULL, "found to delete");
|
||||
cl.b = b;
|
||||
cl.res = ResFAIL;
|
||||
ABQIterate(&abq, TestDeleteCallback, &cl);
|
||||
cdie(cl.res == ResOK, "ABQIterate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
int i;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
abqSize = 0;
|
||||
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none),
|
||||
"mps_arena_create");
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, sizeof(TestBlockStruct));
|
||||
die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
die(ABQInit((Arena)arena, &abq, NULL, ABQ_SIZE, sizeof(TestBlock)),
|
||||
"ABQInit");
|
||||
|
||||
abqSize = ABQ_SIZE;
|
||||
|
||||
for (i = 0; i < TEST_ITER; i++) {
|
||||
step();
|
||||
}
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
240
mps/code/addrobj.c
Normal file
240
mps/code/addrobj.c
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/* addrobj.c: BASE ADDRESS FROM INTERIOR POINTER TEST
|
||||
*
|
||||
* Copyright (c) 2023 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview This test is for mps_addr_object(). Its intention is to
|
||||
* verify that the function returns the appropriate base pointer to an
|
||||
* object when provided with an interior pointer. It also tests that the
|
||||
* function fails appropriately when the provided with a pointer to
|
||||
* unmanaged memory, or to an object in a pool that doesn't support this
|
||||
* feature.
|
||||
*
|
||||
* .limitations Objects that have been moved should cause the function to
|
||||
* fail with MPS_RES_FAIL, however this is not tested. It could be tested if
|
||||
* a testbench deliberately created a forwarding object, however this might
|
||||
* confuse a pool that does automatic garbage collection such as AMC or AMCZ,
|
||||
* so any such test would need to be designed to handle that.
|
||||
* This test only examines behaviour in AMCZ and MVFF pools, i.e. A pool (AMCZ)
|
||||
* which currently implements mps_addr_object() and one (MVFF) that doesn't.
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpscmvff.h"
|
||||
#include "stdio.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Define an object size to allocate. The size chosen doesn't matter much, except that this testbench assumes
|
||||
that the object is large enough that a pointer could point to the interior of the object, without also
|
||||
pointing to the base pointer of the object at the same time. For char pointers, this is probably 2 bytes.
|
||||
Since we are using the Dylan library, we define the size of the object in terms of Dylan slots. See
|
||||
fmtdytst.c for details of the Dylan object structure.*/
|
||||
#define N_SLOT_TESTOBJ 100
|
||||
|
||||
static void test_main(void)
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_pool_t amcz_pool, mvff_pool;
|
||||
mps_ap_t obj_ap;
|
||||
mps_fmt_t obj_fmt;
|
||||
mps_root_t testobj_root;
|
||||
mps_res_t res;
|
||||
/* In another testbench (extcon.c) we observed unreliable failures to do with registering the cold end
|
||||
of the stack. See GitHub issue #210
|
||||
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
|
||||
declare this as a separate root. */
|
||||
static mps_addr_t testobj;
|
||||
mps_addr_t out, in;
|
||||
|
||||
/* Create arena */
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "mps_arena_create_k");
|
||||
|
||||
|
||||
/* INTRO TO TESTS: There are several tests. They test the expected "normal" operation of the
|
||||
function, using an interior pointer, also corner cases where the interior pointer equals the
|
||||
base pointer, where it equals the limit pointer. We also test asking about an address in unmanaged
|
||||
memory, and about an address in a pool which currently does not support mps_addr_object. If you write
|
||||
more tests, describe them here.*/
|
||||
|
||||
|
||||
/* TEST 1: Test using an interior pointer in an object in an AMCZ pool.
|
||||
At the time of writing this test, the AMCZ pool is the only pool where
|
||||
there exists a requirement to provide base addresses from interior pointers.
|
||||
Currently, the AMCZ pool (and by extension, the AMC pool which shares the same
|
||||
module as AMCZ) is the only pool for which mps_addr_object is implemented */
|
||||
|
||||
/* Use the dylan format for convenience */
|
||||
die(dylan_fmt(&obj_fmt, arena), "dylan_fmt");
|
||||
|
||||
/* Create the pool */
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
|
||||
die(mps_pool_create_k(&amcz_pool, arena, mps_class_amcz(), args), "mps_pool_create_k amcz");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
/* Create an area of ambiguous pointers to keep the object alive and in place, in this case
|
||||
the area only contains room for a single reference since we are only using one object to test */
|
||||
die(mps_root_create_area(&testobj_root, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&testobj, &testobj+1,
|
||||
mps_scan_area, NULL),
|
||||
"mps_root_create_area");
|
||||
|
||||
/* Create the allocation point */
|
||||
die(mps_ap_create_k(&obj_ap, amcz_pool, mps_args_none), "mps_ap_create_k");
|
||||
|
||||
/* Make a Dylan object, size = (N_SLOT_TESTOBJ+2) * sizeof(mps_word_t).
|
||||
(See fmtdytst.c for size calculation) */
|
||||
{
|
||||
/* Because make_dylan_vector returns its pointer-to-object as an mps_word_t rather than an
|
||||
mps_addr_t, and commits the object, we need to somehow safely allocate our object without
|
||||
type punning and without risking that our object be destroyed.
|
||||
Rather than redefine our reference table with type mps_word_t, which hides the intention of the table,
|
||||
park the arena to disable garbage collection. Allocate our dylan object on the (unregistered) stack
|
||||
storing its address in an mps_word_t. Then store this mps_word_t as an mps_addr_t in our reference
|
||||
table, and release the arena since our object is now safely pinned.
|
||||
Another approach would be to create another static registered root for ambiguous references of type
|
||||
mps_word_t and then copy to the mps_addr_t root, which would avoid needing to park the arena.
|
||||
*/
|
||||
mps_word_t p_word;
|
||||
mps_arena_park(arena);
|
||||
die(make_dylan_vector(&p_word, obj_ap, N_SLOT_TESTOBJ), "make_dylan_vector");
|
||||
/* If we hadn't parked the arena, our vector might have been GC'd here */
|
||||
testobj = (mps_addr_t)p_word;
|
||||
mps_arena_release(arena);
|
||||
}
|
||||
|
||||
/* Construct a pointer to roughly halfway inside the object */
|
||||
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ/2) * sizeof(mps_word_t));
|
||||
|
||||
/* Ensure that this is an interior pointer, and not the base pointer,
|
||||
since we want to make sure we are testing with a true interior pointer and not
|
||||
one that also happens to be the base pointer. This Insist is intended to protect
|
||||
against the testbench losing its ability to test "true" interior pointers (i.e. ones
|
||||
which don't match the base pointer) if the test object sizes were changed to be very
|
||||
small. Note that we don't currently consider the "limit" of the object as a corner case
|
||||
(so we don't Insist(in != limit) ) but we do consider limit+1, i.e. the pointer to the
|
||||
next object to be a corner case. This test could be updated to consider in == limit as a
|
||||
corner case. */
|
||||
Insist(in > testobj);
|
||||
|
||||
/* Do Test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
Insist(out == testobj);
|
||||
Insist(res == MPS_RES_OK);
|
||||
printf("Interior pointer input: passed\n");
|
||||
|
||||
|
||||
/* TEST 2: Test using the base pointer itself as an input*/
|
||||
|
||||
in = testobj;
|
||||
|
||||
/* Do Test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
Insist(out == testobj);
|
||||
Insist(res == MPS_RES_OK);
|
||||
printf("Base pointer input: passed\n");
|
||||
|
||||
|
||||
|
||||
/* TEST 3: Test using a pointer one-off-the-end of the object*/
|
||||
|
||||
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ + 2) * sizeof(mps_word_t));
|
||||
|
||||
/* Do Test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
Insist(res == MPS_RES_FAIL);
|
||||
printf("Pointer to next object input: passed\n");
|
||||
|
||||
|
||||
/* Clean up from above tests */
|
||||
mps_root_destroy(testobj_root);
|
||||
mps_ap_destroy(obj_ap);
|
||||
mps_pool_destroy(amcz_pool);
|
||||
mps_fmt_destroy(obj_fmt);
|
||||
|
||||
|
||||
/* TEST 4: Test using a pointer in unmanaged memory */
|
||||
|
||||
/* Use malloc to allocate non-mps-managed memory on the heap */
|
||||
in = malloc(sizeof(mps_word_t));
|
||||
Insist(NULL != in);
|
||||
|
||||
/* Do the test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
|
||||
/* Expect MPS to fail to find a base pointer for addresses not in managed memory */
|
||||
Insist(res == MPS_RES_FAIL);
|
||||
printf("Pointer to unmanaged memory input: passed\n");
|
||||
|
||||
/* clean up from this test */
|
||||
if (NULL != in)
|
||||
free(in);
|
||||
|
||||
|
||||
/* TEST 5: Test using a pointer in a pool which currently doesn't implement mps_addr_object */
|
||||
|
||||
/* Create mvff pool for which mps_addr_object is not implemented */
|
||||
die(mps_pool_create_k(&mvff_pool, arena, mps_class_mvff(), mps_args_none), "mps_pool_create_k mvff");
|
||||
|
||||
/* allocate an object (just some memory) in this pool */
|
||||
die(mps_alloc(&in, mvff_pool, sizeof(mps_word_t)), "mps_alloc");
|
||||
|
||||
/* Do the test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
|
||||
Insist(res == MPS_RES_UNIMPL);
|
||||
printf("Pointer to object in pool where mps_addr_object not implemented: passed\n");
|
||||
|
||||
|
||||
/* If more tests are added here, briefly describe them above under "INTRO TO TESTS" comment */
|
||||
|
||||
/* Final clean up */
|
||||
mps_free(mvff_pool, in, sizeof(mps_word_t));
|
||||
mps_pool_destroy(mvff_pool);
|
||||
mps_arena_destroy(arena);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
testlib_init(argc, argv);
|
||||
|
||||
test_main();
|
||||
|
||||
printf("%s: Conclusion, failed to find any defects.\n", argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2022-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
191
mps/code/airtest.c
Normal file
191
mps/code/airtest.c
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/* airtest.c: AMBIGUOUS INTERIOR REFERENCE TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2014-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview: This test case creates a bunch of vectors, registers
|
||||
* them for finalization, and then discards the base pointers to those
|
||||
* objects, keeping only ambiguous interior references to the vector
|
||||
* entries in the stack-allocated table s.
|
||||
*
|
||||
* .options: The test has two options:
|
||||
*
|
||||
* 'interior' is the value passed as MPS_KEY_INTERIOR when creating
|
||||
* the AMC pool. If TRUE, interior pointers must keep objects alive,
|
||||
* and so if any of these objects are finalized, the test fails. If
|
||||
* FALSE, interior pointers do not keep objects alive, so it is likely
|
||||
* that all the objects will be finalized.
|
||||
*
|
||||
* 'stack' is TRUE if the C stack is registered as a root. (If FALSE,
|
||||
* we register the table of interior pointers as an ambiguous root.)
|
||||
*
|
||||
* .fail.lii6ll: The test case passes on most platforms with
|
||||
* interior=FALSE and stack=TRUE (that is, all vectors get finalized),
|
||||
* but fails on lii6ll in variety HOT. Rather than struggle to defeat
|
||||
* the Clang optimizer, we choose not to test in this configuration.
|
||||
* In any case, the MPS does not guarantee anything about timely
|
||||
* finalization <manual/html/topic/finalization.html#cautions>.
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpslib.h"
|
||||
#include "testlib.h"
|
||||
#include "fmtscheme.h"
|
||||
|
||||
#define OBJ_LEN (1u << 4)
|
||||
#define OBJ_COUNT 10
|
||||
|
||||
static void test_air(int interior, int stack)
|
||||
{
|
||||
size_t n_finalized = 0;
|
||||
size_t i, j;
|
||||
obj_t *s[OBJ_COUNT] = {0};
|
||||
mps_root_t root = NULL;
|
||||
if (!stack) {
|
||||
mps_addr_t *p = (void *)s;
|
||||
die(mps_root_create_table(&root, scheme_arena, mps_rank_ambig(), 0, p,
|
||||
OBJ_COUNT), "mps_root_create_table");
|
||||
}
|
||||
mps_message_type_enable(scheme_arena, mps_message_type_finalization());
|
||||
for (j = 0; j < OBJ_COUNT; ++j) {
|
||||
obj_t n = scheme_make_integer(obj_ap, (long)j);
|
||||
obj_t obj = scheme_make_vector(obj_ap, OBJ_LEN, n);
|
||||
mps_addr_t ref = obj;
|
||||
mps_finalize(scheme_arena, &ref);
|
||||
s[j] = obj->vector.vector;
|
||||
}
|
||||
for (i = 1; i < OBJ_LEN; ++i) {
|
||||
obj_t n = scheme_make_integer(obj_ap, (long)i);
|
||||
mps_message_t msg;
|
||||
for (j = 0; j + 1 < OBJ_COUNT; ++j) {
|
||||
*++s[j] = n;
|
||||
}
|
||||
mps_arena_collect(scheme_arena);
|
||||
mps_arena_release(scheme_arena);
|
||||
if (mps_message_get(&msg, scheme_arena, mps_message_type_finalization())) {
|
||||
mps_addr_t ref;
|
||||
mps_message_finalization_ref(&ref, scheme_arena, msg);
|
||||
++ n_finalized;
|
||||
if (interior) {
|
||||
obj_t o;
|
||||
o = ref;
|
||||
error("wrongly finalized vector %ld at %p",
|
||||
o->vector.vector[0]->integer.integer, (void *)o);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!interior && n_finalized < OBJ_COUNT) {
|
||||
error("only finalized %"PRIuLONGEST" out of %"PRIuLONGEST" vectors.",
|
||||
(ulongest_t)n_finalized, (ulongest_t)OBJ_COUNT);
|
||||
}
|
||||
if (!stack) {
|
||||
mps_root_destroy(root);
|
||||
}
|
||||
}
|
||||
|
||||
static mps_gen_param_s obj_gen_params[] = {
|
||||
{ 150, 0.85 },
|
||||
{ 170, 0.45 }
|
||||
};
|
||||
|
||||
static void test_main(void *marker, int interior, int stack)
|
||||
{
|
||||
mps_res_t res;
|
||||
mps_chain_t obj_chain;
|
||||
mps_fmt_t obj_fmt;
|
||||
mps_thr_t thread;
|
||||
mps_root_t reg_root = NULL;
|
||||
|
||||
res = mps_arena_create_k(&scheme_arena, mps_arena_class_vm(), mps_args_none);
|
||||
if (res != MPS_RES_OK)
|
||||
error("Couldn't create arena");
|
||||
|
||||
res = mps_chain_create(&obj_chain, scheme_arena,
|
||||
sizeof(obj_gen_params) / sizeof(*obj_gen_params),
|
||||
obj_gen_params);
|
||||
if (res != MPS_RES_OK)
|
||||
error("Couldn't create obj chain");
|
||||
|
||||
scheme_fmt(&obj_fmt);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, obj_chain);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_INTERIOR, interior);
|
||||
die(mps_pool_create_k(&obj_pool, scheme_arena, mps_class_amc(), args),
|
||||
"mps_pool_create_k");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
res = mps_ap_create_k(&obj_ap, obj_pool, mps_args_none);
|
||||
if (res != MPS_RES_OK)
|
||||
error("Couldn't create obj allocation point");
|
||||
|
||||
res = mps_thread_reg(&thread, scheme_arena);
|
||||
if (res != MPS_RES_OK)
|
||||
error("Couldn't register thread");
|
||||
|
||||
if (stack) {
|
||||
res = mps_root_create_thread(®_root, scheme_arena, thread, marker);
|
||||
if (res != MPS_RES_OK)
|
||||
error("Couldn't create root");
|
||||
}
|
||||
|
||||
test_air(interior, stack);
|
||||
|
||||
mps_arena_park(scheme_arena);
|
||||
if (stack)
|
||||
mps_root_destroy(reg_root);
|
||||
mps_thread_dereg(thread);
|
||||
mps_ap_destroy(obj_ap);
|
||||
mps_pool_destroy(obj_pool);
|
||||
mps_chain_destroy(obj_chain);
|
||||
mps_fmt_destroy(obj_fmt);
|
||||
mps_arena_destroy(scheme_arena);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *marker = ▮
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
test_main(marker, TRUE, TRUE);
|
||||
test_main(marker, TRUE, FALSE);
|
||||
/* not test_main(marker, FALSE, TRUE) -- see .fail.lii6ll. */
|
||||
test_main(marker, FALSE, FALSE);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2014-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
385
mps/code/amcss.c
Normal file
385
mps/code/amcss.c
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
/* amcss.c: POOL CLASS AMC STRESS TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*/
|
||||
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "testlib.h"
|
||||
#include "mpm.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpstd.h"
|
||||
#include "mps.h"
|
||||
#include "mpslib.h"
|
||||
|
||||
#include <stdio.h> /* fflush, printf, putchar */
|
||||
|
||||
|
||||
/* These values have been tuned in the hope of getting one dynamic collection. */
|
||||
#define testArenaSIZE ((size_t)1000*1024)
|
||||
#define gen1SIZE ((size_t)20)
|
||||
#define gen2SIZE ((size_t)85)
|
||||
#define avLEN 3
|
||||
#define exactRootsCOUNT 180
|
||||
#define ambigRootsCOUNT 50
|
||||
#define genCOUNT 2
|
||||
#define collectionsCOUNT 37
|
||||
#define rampSIZE 9
|
||||
#define initTestFREQ 6000
|
||||
|
||||
/* testChain -- generation parameters for the test */
|
||||
|
||||
static mps_gen_param_s testChain[genCOUNT] = {
|
||||
{ gen1SIZE, 0.85 }, { gen2SIZE, 0.45 } };
|
||||
|
||||
|
||||
/* objNULL needs to be odd so that it's ignored in exactRoots. */
|
||||
#define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED))
|
||||
|
||||
|
||||
static mps_arena_t arena;
|
||||
static mps_ap_t ap;
|
||||
static mps_addr_t exactRoots[exactRootsCOUNT];
|
||||
static mps_addr_t ambigRoots[ambigRootsCOUNT];
|
||||
static size_t scale; /* Overall scale factor. */
|
||||
static unsigned long nCollsStart;
|
||||
static unsigned long nCollsDone;
|
||||
|
||||
|
||||
/* report -- report statistics from any messages */
|
||||
|
||||
static void report(void)
|
||||
{
|
||||
mps_message_type_t type;
|
||||
|
||||
while(mps_message_queue_type(&type, arena)) {
|
||||
mps_message_t message;
|
||||
|
||||
cdie(mps_message_get(&message, arena, type), "message get");
|
||||
|
||||
if (type == mps_message_type_gc_start()) {
|
||||
nCollsStart += 1;
|
||||
printf("\n{\n Collection %lu started. Because:\n", nCollsStart);
|
||||
printf(" %s\n", mps_message_gc_start_why(arena, message));
|
||||
printf(" clock: %"PRIuLONGEST"\n", (ulongest_t)mps_message_clock(arena, message));
|
||||
|
||||
} else if (type == mps_message_type_gc()) {
|
||||
size_t live, condemned, not_condemned;
|
||||
|
||||
nCollsDone += 1;
|
||||
live = mps_message_gc_live_size(arena, message);
|
||||
condemned = mps_message_gc_condemned_size(arena, message);
|
||||
not_condemned = mps_message_gc_not_condemned_size(arena, message);
|
||||
|
||||
printf("\n Collection %lu finished:\n", nCollsDone);
|
||||
printf(" live %"PRIuLONGEST"\n", (ulongest_t)live);
|
||||
printf(" condemned %"PRIuLONGEST"\n", (ulongest_t)condemned);
|
||||
printf(" not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
|
||||
printf(" clock: %"PRIuLONGEST"\n", (ulongest_t)mps_message_clock(arena, message));
|
||||
printf("}\n");
|
||||
} else {
|
||||
cdie(0, "unknown message type");
|
||||
break;
|
||||
}
|
||||
|
||||
mps_message_discard(arena, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* make -- create one new object */
|
||||
|
||||
static mps_addr_t make(size_t rootsCount)
|
||||
{
|
||||
/* The calls variable is useful when debugging to stop the debugging
|
||||
after a certain number of allocations using a debugger
|
||||
"watchpoint". Allocations are a good "clock tick" for this test.
|
||||
The test itself doesn't use the variable, so we declare it
|
||||
volatile, which also forces updates into memory. */
|
||||
static volatile unsigned long calls = 0;
|
||||
size_t length = rnd() % (scale * avLEN);
|
||||
size_t size = (length+2) * sizeof(mps_word_t);
|
||||
mps_addr_t p;
|
||||
mps_res_t res;
|
||||
++ calls;
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, p, ap, size);
|
||||
if (res) {
|
||||
ArenaDescribe(arena, mps_lib_get_stderr(), 4);
|
||||
die(res, "MPS_RESERVE_BLOCK");
|
||||
}
|
||||
res = dylan_init(p, size, exactRoots, rootsCount);
|
||||
if (res)
|
||||
die(res, "dylan_init");
|
||||
} while(!mps_commit(ap, p, size));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* test_stepper -- stepping function for walk */
|
||||
|
||||
static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool,
|
||||
void *p, size_t s)
|
||||
{
|
||||
testlib_unused(object); testlib_unused(fmt); testlib_unused(pool);
|
||||
testlib_unused(s);
|
||||
(*(unsigned long *)p)++;
|
||||
}
|
||||
|
||||
|
||||
/* area_scan -- area scanning function for mps_pool_walk */
|
||||
|
||||
static mps_res_t area_scan(mps_ss_t ss, void *base, void *limit, void *closure)
|
||||
{
|
||||
unsigned long *count = closure;
|
||||
mps_res_t res;
|
||||
while (base < limit) {
|
||||
mps_addr_t prev = base;
|
||||
++ *count;
|
||||
res = dylan_scan1(ss, &base);
|
||||
if (res != MPS_RES_OK) return res;
|
||||
Insist(prev < base);
|
||||
}
|
||||
Insist(base == limit);
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
/* test -- the body of the test */
|
||||
|
||||
static void test(mps_pool_class_t pool_class, size_t roots_count)
|
||||
{
|
||||
mps_fmt_t format;
|
||||
mps_chain_t chain;
|
||||
mps_root_t exactRoot, ambigRoot;
|
||||
unsigned long objs; size_t i;
|
||||
mps_word_t collections, rampSwitch;
|
||||
mps_alloc_pattern_t ramp = mps_alloc_pattern_ramp();
|
||||
int ramping;
|
||||
mps_ap_t busy_ap;
|
||||
mps_addr_t busy_init;
|
||||
mps_pool_t pool;
|
||||
int described = 0;
|
||||
|
||||
die(dylan_fmt(&format, arena), "fmt_create");
|
||||
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
|
||||
|
||||
die(mps_pool_create(&pool, arena, pool_class, format, chain),
|
||||
"pool_create(amc)");
|
||||
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
|
||||
die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2");
|
||||
|
||||
for(i = 0; i < exactRootsCOUNT; ++i)
|
||||
exactRoots[i] = objNULL;
|
||||
for(i = 0; i < ambigRootsCOUNT; ++i)
|
||||
ambigRoots[i] = rnd_addr();
|
||||
|
||||
die(mps_root_create_table_masked(&exactRoot, arena,
|
||||
mps_rank_exact(), (mps_rm_t)0,
|
||||
&exactRoots[0], exactRootsCOUNT,
|
||||
(mps_word_t)1),
|
||||
"root_create_table(exact)");
|
||||
die(mps_root_create_table(&ambigRoot, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&ambigRoots[0], ambigRootsCOUNT),
|
||||
"root_create_table(ambig)");
|
||||
|
||||
/* create an ap, and leave it busy */
|
||||
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
|
||||
|
||||
nCollsStart = 0;
|
||||
nCollsDone = 0;
|
||||
collections = 0;
|
||||
rampSwitch = rampSIZE;
|
||||
die(mps_ap_alloc_pattern_begin(ap, ramp), "pattern begin (ap)");
|
||||
die(mps_ap_alloc_pattern_begin(busy_ap, ramp), "pattern begin (busy_ap)");
|
||||
ramping = 1;
|
||||
objs = 0;
|
||||
while (collections < collectionsCOUNT) {
|
||||
size_t r;
|
||||
|
||||
report();
|
||||
if (collections != nCollsStart) {
|
||||
if (!described) {
|
||||
die(ArenaDescribe(arena, mps_lib_get_stdout(), 0), "ArenaDescribe");
|
||||
described = TRUE;
|
||||
}
|
||||
collections = nCollsStart;
|
||||
|
||||
printf("%lu objects (nCollsStart=%"PRIuLONGEST")\n", objs,
|
||||
(ulongest_t)collections);
|
||||
|
||||
/* test mps_arena_has_addr */
|
||||
{
|
||||
size_t hitRatio;
|
||||
unsigned hitsWanted = 4; /* aim for 4 hits (on average) */
|
||||
/* [Note: The for-loop condition used to be "i < 4 * hitRatio",
|
||||
* with "4" an unexplained naked constant. I have now labelled
|
||||
* it "hitsWanted", as I think that is the intent. RHSK]
|
||||
*/
|
||||
|
||||
/* how many random addrs must we try, to hit the arena once? */
|
||||
hitRatio = (0xfffffffful / mps_arena_committed(arena));
|
||||
for (i = 0; i < hitsWanted * hitRatio ; i++) {
|
||||
/* An exact root maybe in the arena, so add a random 32-bit
|
||||
* offset to it. We may get no hits if it is objNULL.
|
||||
*/
|
||||
mps_addr_t p = (char *)exactRoots[rnd() % exactRootsCOUNT]
|
||||
+ rnd()-0x80000000ul;
|
||||
if (mps_arena_has_addr(arena, p)) {
|
||||
printf("%p is in the arena\n", p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < exactRootsCOUNT; ++i)
|
||||
cdie(exactRoots[i] == objNULL
|
||||
|| (dylan_check(exactRoots[i])
|
||||
&& mps_arena_has_addr(arena, exactRoots[i])),
|
||||
"all roots check");
|
||||
cdie(!mps_arena_has_addr(arena, NULL),
|
||||
"NULL in arena");
|
||||
|
||||
if (collections == collectionsCOUNT / 2) {
|
||||
unsigned long count1 = 0, count2 = 0;
|
||||
mps_arena_park(arena);
|
||||
mps_arena_formatted_objects_walk(arena, test_stepper, &count1, 0);
|
||||
die(mps_pool_walk(pool, area_scan, &count2), "mps_pool_walk");
|
||||
mps_arena_release(arena);
|
||||
printf("stepped on %lu objects.\n", count1);
|
||||
printf("walked %lu objects.\n", count2);
|
||||
Insist(count1 == count2);
|
||||
}
|
||||
if (collections == rampSwitch) {
|
||||
int begin_ramp = !ramping
|
||||
|| /* Every other time, switch back immediately. */ (collections & 1);
|
||||
|
||||
rampSwitch += rampSIZE;
|
||||
if (ramping) {
|
||||
die(mps_ap_alloc_pattern_end(ap, ramp), "pattern end (ap)");
|
||||
die(mps_ap_alloc_pattern_end(busy_ap, ramp), "pattern end (busy_ap)");
|
||||
ramping = 0;
|
||||
/* kill half of the roots */
|
||||
for(i = 0; i < exactRootsCOUNT; i += 2) {
|
||||
if (exactRoots[i] != objNULL) {
|
||||
cdie(dylan_check(exactRoots[i]), "ramp kill check");
|
||||
exactRoots[i] = objNULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (begin_ramp) {
|
||||
die(mps_ap_alloc_pattern_begin(ap, ramp),
|
||||
"pattern rebegin (ap)");
|
||||
die(mps_ap_alloc_pattern_begin(busy_ap, ramp),
|
||||
"pattern rebegin (busy_ap)");
|
||||
ramping = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = (size_t)rnd();
|
||||
if (r & 1) {
|
||||
i = (r >> 1) % exactRootsCOUNT;
|
||||
if (exactRoots[i] != objNULL)
|
||||
cdie(dylan_check(exactRoots[i]), "dying root check");
|
||||
exactRoots[i] = make(roots_count);
|
||||
if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
|
||||
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
|
||||
exactRoots, exactRootsCOUNT);
|
||||
} else {
|
||||
i = (r >> 1) % ambigRootsCOUNT;
|
||||
ambigRoots[(ambigRootsCOUNT-1) - i] = make(roots_count);
|
||||
/* Create random interior pointers */
|
||||
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
|
||||
}
|
||||
|
||||
if (r % initTestFREQ == 0)
|
||||
*(int*)busy_init = -1; /* check that the buffer is still there */
|
||||
|
||||
if (objs % 1024 == 0) {
|
||||
report();
|
||||
putchar('.');
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
++objs;
|
||||
}
|
||||
|
||||
(void)mps_commit(busy_ap, busy_init, 64);
|
||||
mps_arena_park(arena);
|
||||
mps_ap_destroy(busy_ap);
|
||||
mps_ap_destroy(ap);
|
||||
mps_root_destroy(exactRoot);
|
||||
mps_root_destroy(ambigRoot);
|
||||
mps_pool_destroy(pool);
|
||||
mps_chain_destroy(chain);
|
||||
mps_fmt_destroy(format);
|
||||
mps_arena_release(arena);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
size_t i, grainSize;
|
||||
mps_thr_t thread;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
scale = (size_t)1 << (rnd() % 6);
|
||||
for (i = 0; i < genCOUNT; ++i) testChain[i].mps_capacity *= scale;
|
||||
grainSize = rnd_grain(scale * testArenaSIZE);
|
||||
printf("Picked scale=%lu grainSize=%lu\n", (unsigned long)scale, (unsigned long)grainSize);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, scale * testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, grainSize);
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
|
||||
} MPS_ARGS_END(args);
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
mps_message_type_enable(arena, mps_message_type_gc_start());
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
test(mps_class_amc(), exactRootsCOUNT);
|
||||
test(mps_class_amcz(), 0);
|
||||
mps_thread_dereg(thread);
|
||||
report();
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
295
mps/code/amcsshe.c
Normal file
295
mps/code/amcsshe.c
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/* amcsshe.c: POOL CLASS AMC STRESS TEST WITH HEADER
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (c) 2002 Global Graphics Software.
|
||||
*/
|
||||
|
||||
#include "fmthe.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpstd.h"
|
||||
#include "mps.h"
|
||||
|
||||
#include <stdio.h> /* fflush, printf, putchar */
|
||||
|
||||
|
||||
/* These values have been tuned in the hope of getting one dynamic collection. */
|
||||
#define headerFACTOR ((float)(20 + headerSIZE) / 20)
|
||||
/* headerFACTOR measures how much larger objects are compared to fmtdy. */
|
||||
#define testArenaSIZE ((size_t)(2000*headerFACTOR)*1024)
|
||||
#define gen1SIZE ((size_t)(150*headerFACTOR))
|
||||
#define gen2SIZE ((size_t)(170*headerFACTOR))
|
||||
#define avLEN 3
|
||||
#define exactRootsCOUNT 200
|
||||
#define ambigRootsCOUNT 50
|
||||
#define bogusRootsCOUNT 4096
|
||||
#define collectionsCOUNT 37
|
||||
#define rampSIZE 9
|
||||
#define initTestFREQ 6000
|
||||
#define genCOUNT 2
|
||||
|
||||
/* testChain -- generation parameters for the test */
|
||||
|
||||
static mps_gen_param_s testChain[genCOUNT] = {
|
||||
{ gen1SIZE, 0.85 }, { gen2SIZE, 0.45 } };
|
||||
|
||||
|
||||
/* objNULL needs to be odd so that it's ignored in exactRoots. */
|
||||
#define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED))
|
||||
|
||||
|
||||
static mps_pool_t pool;
|
||||
static mps_ap_t ap;
|
||||
static mps_addr_t exactRoots[exactRootsCOUNT];
|
||||
static mps_addr_t ambigRoots[ambigRootsCOUNT];
|
||||
static mps_addr_t bogusRoots[bogusRootsCOUNT];
|
||||
|
||||
static mps_addr_t make(size_t roots_count)
|
||||
{
|
||||
size_t length = rnd() % (2*avLEN);
|
||||
size_t size = (length+2) * sizeof(mps_word_t);
|
||||
mps_addr_t p, userP;
|
||||
mps_res_t res;
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, p, ap, size + headerSIZE);
|
||||
if (res)
|
||||
die(res, "MPS_RESERVE_BLOCK");
|
||||
userP = (mps_addr_t)((char*)p + headerSIZE);
|
||||
res = dylan_init(userP, size, exactRoots, roots_count);
|
||||
if (res)
|
||||
die(res, "dylan_init");
|
||||
((int*)p)[0] = realHeader;
|
||||
((int*)p)[1] = 0xED0ED;
|
||||
} while(!mps_commit(ap, p, size + headerSIZE));
|
||||
|
||||
return userP;
|
||||
}
|
||||
|
||||
|
||||
/* report - report statistics from any terminated GCs */
|
||||
|
||||
static void report(mps_arena_t arena)
|
||||
{
|
||||
mps_message_t message;
|
||||
static int nCollections = 0;
|
||||
|
||||
while (mps_message_get(&message, arena, mps_message_type_gc())) {
|
||||
size_t live, condemned, not_condemned;
|
||||
|
||||
live = mps_message_gc_live_size(arena, message);
|
||||
condemned = mps_message_gc_condemned_size(arena, message);
|
||||
not_condemned = mps_message_gc_not_condemned_size(arena, message);
|
||||
|
||||
printf("\nCollection %d finished:\n", ++nCollections);
|
||||
printf("live %"PRIuLONGEST"\n", (ulongest_t)live);
|
||||
printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned);
|
||||
printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
|
||||
|
||||
mps_message_discard(arena, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* test -- the body of the test */
|
||||
|
||||
static void *test(mps_arena_t arena, mps_pool_class_t pool_class,
|
||||
size_t roots_count)
|
||||
{
|
||||
mps_fmt_t format;
|
||||
mps_chain_t chain;
|
||||
mps_root_t exactRoot, ambigRoot, bogusRoot;
|
||||
unsigned long objs; size_t i;
|
||||
mps_word_t collections, rampSwitch;
|
||||
mps_alloc_pattern_t ramp = mps_alloc_pattern_ramp();
|
||||
int ramping;
|
||||
mps_ap_t busy_ap;
|
||||
mps_addr_t busy_init;
|
||||
|
||||
die(EnsureHeaderFormat(&format, arena), "fmt_create");
|
||||
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
|
||||
|
||||
die(mps_pool_create(&pool, arena, pool_class, format, chain),
|
||||
"pool_create(amc)");
|
||||
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
|
||||
die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2");
|
||||
|
||||
for(i = 0; i < exactRootsCOUNT; ++i)
|
||||
exactRoots[i] = objNULL;
|
||||
for(i = 0; i < ambigRootsCOUNT; ++i)
|
||||
ambigRoots[i] = rnd_addr();
|
||||
|
||||
die(mps_root_create_table_masked(&exactRoot, arena,
|
||||
mps_rank_exact(), (mps_rm_t)0,
|
||||
&exactRoots[0], exactRootsCOUNT,
|
||||
(mps_word_t)1),
|
||||
"root_create_table(exact)");
|
||||
die(mps_root_create_table(&ambigRoot, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&ambigRoots[0], ambigRootsCOUNT),
|
||||
"root_create_table(ambig)");
|
||||
die(mps_root_create_table(&bogusRoot, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&bogusRoots[0], bogusRootsCOUNT),
|
||||
"root_create_table(bogus)");
|
||||
|
||||
/* create an ap, and leave it busy */
|
||||
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
|
||||
|
||||
collections = 0;
|
||||
rampSwitch = rampSIZE;
|
||||
die(mps_ap_alloc_pattern_begin(ap, ramp), "pattern begin (ap)");
|
||||
die(mps_ap_alloc_pattern_begin(busy_ap, ramp), "pattern begin (busy_ap)");
|
||||
ramping = 1;
|
||||
objs = 0;
|
||||
while (collections < collectionsCOUNT) {
|
||||
mps_word_t c;
|
||||
size_t r;
|
||||
|
||||
c = mps_collections(arena);
|
||||
|
||||
if (collections != c) {
|
||||
collections = c;
|
||||
printf("\nCollection %"PRIuLONGEST", %lu objects.\n",
|
||||
(ulongest_t)c, objs);
|
||||
report(arena);
|
||||
for (r = 0; r < exactRootsCOUNT; ++r) {
|
||||
if (exactRoots[r] != objNULL)
|
||||
die(HeaderFormatCheck(exactRoots[r]), "wrapper check");
|
||||
}
|
||||
if (collections == rampSwitch) {
|
||||
int begin_ramp = !ramping
|
||||
|| /* Every other time, switch back immediately. */ (collections & 1);
|
||||
|
||||
rampSwitch += rampSIZE;
|
||||
if (ramping) {
|
||||
die(mps_ap_alloc_pattern_end(ap, ramp), "pattern end (ap)");
|
||||
die(mps_ap_alloc_pattern_end(busy_ap, ramp), "pattern end (busy_ap)");
|
||||
ramping = 0;
|
||||
/* kill half of the roots */
|
||||
for(i = 0; i < exactRootsCOUNT; i += 2) {
|
||||
if (exactRoots[i] != objNULL) {
|
||||
die(HeaderFormatCheck(exactRoots[i]), "ramp kill check");
|
||||
exactRoots[i] = objNULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (begin_ramp) {
|
||||
die(mps_ap_alloc_pattern_begin(ap, ramp),
|
||||
"pattern rebegin (ap)");
|
||||
die(mps_ap_alloc_pattern_begin(busy_ap, ramp),
|
||||
"pattern rebegin (busy_ap)");
|
||||
ramping = 1;
|
||||
}
|
||||
}
|
||||
/* fill bogusRoots with variations of a real pointer */
|
||||
r = rnd() % exactRootsCOUNT;
|
||||
if (exactRoots[r] != objNULL) {
|
||||
char *p = (char*)exactRoots[r];
|
||||
|
||||
for(i = 0; i < bogusRootsCOUNT; ++i)
|
||||
bogusRoots[i] = (mps_addr_t)(p + i);
|
||||
}
|
||||
}
|
||||
|
||||
r = (size_t)rnd();
|
||||
if (r & 1) {
|
||||
i = (r >> 1) % exactRootsCOUNT;
|
||||
if (exactRoots[i] != objNULL)
|
||||
die(HeaderFormatCheck(exactRoots[i]), "wrapper check");
|
||||
exactRoots[i] = make(roots_count);
|
||||
if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
|
||||
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
|
||||
exactRoots, exactRootsCOUNT);
|
||||
} else {
|
||||
i = (r >> 1) % ambigRootsCOUNT;
|
||||
ambigRoots[(ambigRootsCOUNT-1) - i] = make(roots_count);
|
||||
/* Create random interior pointers */
|
||||
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
|
||||
}
|
||||
|
||||
if (r % initTestFREQ == 0)
|
||||
*(int*)busy_init = -1; /* check that the buffer is still there */
|
||||
|
||||
if (objs % 1024 == 0) {
|
||||
report(arena);
|
||||
putchar('.');
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
++objs;
|
||||
}
|
||||
|
||||
(void)mps_commit(busy_ap, busy_init, 64);
|
||||
mps_arena_park(arena);
|
||||
mps_ap_destroy(busy_ap);
|
||||
mps_ap_destroy(ap);
|
||||
mps_root_destroy(exactRoot);
|
||||
mps_root_destroy(ambigRoot);
|
||||
mps_root_destroy(bogusRoot);
|
||||
mps_pool_destroy(pool);
|
||||
mps_chain_destroy(chain);
|
||||
mps_fmt_destroy(format);
|
||||
mps_arena_release(arena);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thread;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
|
||||
} MPS_ARGS_END(args);
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
test(arena, mps_class_amc(), exactRootsCOUNT);
|
||||
test(arena, mps_class_amcz(), 0);
|
||||
mps_thread_dereg(thread);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
387
mps/code/amcssth.c
Normal file
387
mps/code/amcssth.c
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
/* amcssth.c: POOL CLASS AMC STRESS TEST WITH TWO THREADS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (c) 2002 Global Graphics Software.
|
||||
*
|
||||
* The main thread parks the arena half way through the test case and
|
||||
* runs mps_pool_walk() and mps_arena_formatted_objects_walk(). This
|
||||
* checks that walking works while the other threads continue to
|
||||
* allocate in the background.
|
||||
*/
|
||||
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "testlib.h"
|
||||
#include "testthr.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpsavm.h"
|
||||
|
||||
#include <stdio.h> /* fflush, printf, putchar */
|
||||
|
||||
|
||||
/* These values have been tuned in the hope of getting one dynamic collection. */
|
||||
#define testArenaSIZE ((size_t)1000*1024)
|
||||
#define gen1SIZE ((size_t)150)
|
||||
#define gen2SIZE ((size_t)170)
|
||||
#define avLEN 3
|
||||
#define exactRootsCOUNT 180
|
||||
#define ambigRootsCOUNT 50
|
||||
#define genCOUNT 2
|
||||
#define collectionsCOUNT 37
|
||||
#define rampSIZE 9
|
||||
#define initTestFREQ 6000
|
||||
|
||||
/* testChain -- generation parameters for the test */
|
||||
|
||||
static mps_gen_param_s testChain[genCOUNT] = {
|
||||
{ gen1SIZE, 0.85 }, { gen2SIZE, 0.45 } };
|
||||
|
||||
|
||||
/* objNULL needs to be odd so that it's ignored in exactRoots. */
|
||||
#define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED))
|
||||
|
||||
|
||||
static mps_addr_t exactRoots[exactRootsCOUNT];
|
||||
static mps_addr_t ambigRoots[ambigRootsCOUNT];
|
||||
|
||||
|
||||
static mps_word_t collections;
|
||||
static mps_arena_t arena;
|
||||
static mps_root_t exactRoot, ambigRoot;
|
||||
static unsigned long objs = 0;
|
||||
|
||||
|
||||
/* make -- create one new object */
|
||||
|
||||
static mps_addr_t make(mps_ap_t ap, size_t roots_count)
|
||||
{
|
||||
size_t length = rnd() % (2*avLEN);
|
||||
size_t size = (length+2) * sizeof(mps_word_t);
|
||||
mps_addr_t p;
|
||||
mps_res_t res;
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, p, ap, size);
|
||||
if (res)
|
||||
die(res, "MPS_RESERVE_BLOCK");
|
||||
res = dylan_init(p, size, exactRoots, roots_count);
|
||||
if (res)
|
||||
die(res, "dylan_init");
|
||||
} while(!mps_commit(ap, p, size));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* test_stepper -- stepping function for walk */
|
||||
|
||||
static void test_stepper(mps_addr_t object, mps_fmt_t fmt, mps_pool_t pool,
|
||||
void *p, size_t s)
|
||||
{
|
||||
testlib_unused(object); testlib_unused(fmt); testlib_unused(pool);
|
||||
testlib_unused(s);
|
||||
(*(unsigned long *)p)++;
|
||||
}
|
||||
|
||||
|
||||
/* area_scan -- area scanning function for mps_pool_walk */
|
||||
|
||||
static mps_res_t area_scan(mps_ss_t ss, void *base, void *limit, void *closure)
|
||||
{
|
||||
unsigned long *count = closure;
|
||||
mps_res_t res;
|
||||
while (base < limit) {
|
||||
mps_addr_t prev = base;
|
||||
++ *count;
|
||||
res = dylan_scan1(ss, &base);
|
||||
if (res != MPS_RES_OK) return res;
|
||||
Insist(prev < base);
|
||||
}
|
||||
Insist(base == limit);
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
/* churn -- create an object and install into roots */
|
||||
|
||||
static void churn(mps_ap_t ap, size_t roots_count)
|
||||
{
|
||||
size_t i;
|
||||
size_t r;
|
||||
|
||||
++objs;
|
||||
r = (size_t)rnd();
|
||||
if (r & 1) {
|
||||
i = (r >> 1) % exactRootsCOUNT;
|
||||
if (exactRoots[i] != objNULL)
|
||||
cdie(dylan_check(exactRoots[i]), "dying root check");
|
||||
exactRoots[i] = make(ap, roots_count);
|
||||
if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
|
||||
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
|
||||
exactRoots, exactRootsCOUNT);
|
||||
} else {
|
||||
i = (r >> 1) % ambigRootsCOUNT;
|
||||
ambigRoots[(ambigRootsCOUNT-1) - i] = make(ap, roots_count);
|
||||
/* Create random interior pointers */
|
||||
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef struct closure_s {
|
||||
mps_pool_t pool;
|
||||
size_t roots_count;
|
||||
} closure_s, *closure_t;
|
||||
|
||||
static void *kid_thread(void *arg)
|
||||
{
|
||||
void *marker = ▮
|
||||
mps_thr_t thread1, thread2;
|
||||
mps_root_t reg_root;
|
||||
mps_ap_t ap;
|
||||
closure_t cl = arg;
|
||||
|
||||
/* Register the thread twice to check this is supported -- see
|
||||
* <design/thread-manager#.req.register.multi>
|
||||
*/
|
||||
die(mps_thread_reg(&thread1, arena), "thread_reg");
|
||||
die(mps_thread_reg(&thread2, arena), "thread_reg");
|
||||
die(mps_root_create_thread(®_root, arena, thread1, marker),
|
||||
"root_create");
|
||||
|
||||
die(mps_ap_create(&ap, cl->pool, mps_rank_exact()), "BufferCreate(fooey)");
|
||||
while(mps_collections(arena) < collectionsCOUNT) {
|
||||
churn(ap, cl->roots_count);
|
||||
}
|
||||
mps_ap_destroy(ap);
|
||||
|
||||
mps_root_destroy(reg_root);
|
||||
mps_thread_dereg(thread2);
|
||||
mps_thread_dereg(thread1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* test -- the body of the test */
|
||||
|
||||
static void test_pool(const char *name, mps_pool_t pool, size_t roots_count)
|
||||
{
|
||||
size_t i;
|
||||
mps_word_t rampSwitch;
|
||||
mps_alloc_pattern_t ramp = mps_alloc_pattern_ramp();
|
||||
int ramping;
|
||||
mps_ap_t ap, busy_ap;
|
||||
mps_addr_t busy_init;
|
||||
testthr_t kids[10];
|
||||
closure_s cl;
|
||||
int walked = FALSE, ramped = FALSE;
|
||||
|
||||
printf("\n------ pool: %s-------\n", name);
|
||||
|
||||
cl.pool = pool;
|
||||
cl.roots_count = roots_count;
|
||||
collections = 0;
|
||||
|
||||
for (i = 0; i < NELEMS(kids); ++i)
|
||||
testthr_create(&kids[i], kid_thread, &cl);
|
||||
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
|
||||
die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2");
|
||||
|
||||
/* create an ap, and leave it busy */
|
||||
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
|
||||
|
||||
rampSwitch = rampSIZE;
|
||||
die(mps_ap_alloc_pattern_begin(ap, ramp), "pattern begin (ap)");
|
||||
die(mps_ap_alloc_pattern_begin(busy_ap, ramp), "pattern begin (busy_ap)");
|
||||
ramping = 1;
|
||||
while (collections < collectionsCOUNT) {
|
||||
mps_message_type_t type;
|
||||
|
||||
if (mps_message_queue_type(&type, arena)) {
|
||||
mps_message_t msg;
|
||||
mps_bool_t b = mps_message_get(&msg, arena, type);
|
||||
Insist(b); /* we just checked there was one */
|
||||
|
||||
if (type == mps_message_type_gc()) {
|
||||
size_t live = mps_message_gc_live_size(arena, msg);
|
||||
size_t condemned = mps_message_gc_condemned_size(arena, msg);
|
||||
size_t not_condemned = mps_message_gc_not_condemned_size(arena, msg);
|
||||
|
||||
printf("\nCollection %lu finished:\n", (unsigned long)collections++);
|
||||
printf("live %"PRIuLONGEST"\n", (ulongest_t)live);
|
||||
printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned);
|
||||
printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
|
||||
|
||||
} else if (type == mps_message_type_gc_start()) {
|
||||
printf("\nCollection %lu started, %lu objects, committed=%lu.\n",
|
||||
(unsigned long)collections, objs,
|
||||
(unsigned long)mps_arena_committed(arena));
|
||||
|
||||
for (i = 0; i < exactRootsCOUNT; ++i)
|
||||
cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]),
|
||||
"all roots check");
|
||||
|
||||
if (collections >= collectionsCOUNT / 2 && !walked)
|
||||
{
|
||||
unsigned long count1 = 0, count2 = 0;
|
||||
mps_arena_park(arena);
|
||||
mps_arena_formatted_objects_walk(arena, test_stepper, &count1, 0);
|
||||
die(mps_pool_walk(pool, area_scan, &count2), "mps_pool_walk");
|
||||
mps_arena_release(arena);
|
||||
printf("stepped on %lu objects.\n", count1);
|
||||
printf("walked %lu objects.\n", count2);
|
||||
walked = TRUE;
|
||||
}
|
||||
if (collections >= rampSwitch && !ramped) {
|
||||
/* Every other time, switch back immediately. */
|
||||
int begin_ramp = !ramping || (collections & 1);
|
||||
|
||||
rampSwitch += rampSIZE;
|
||||
if (ramping) {
|
||||
die(mps_ap_alloc_pattern_end(ap, ramp), "pattern end (ap)");
|
||||
die(mps_ap_alloc_pattern_end(busy_ap, ramp),
|
||||
"pattern end (busy_ap)");
|
||||
ramping = 0;
|
||||
/* kill half of the roots */
|
||||
for(i = 0; i < exactRootsCOUNT; i += 2) {
|
||||
if (exactRoots[i] != objNULL) {
|
||||
cdie(dylan_check(exactRoots[i]), "ramp kill check");
|
||||
exactRoots[i] = objNULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (begin_ramp) {
|
||||
die(mps_ap_alloc_pattern_begin(ap, ramp),
|
||||
"pattern rebegin (ap)");
|
||||
die(mps_ap_alloc_pattern_begin(busy_ap, ramp),
|
||||
"pattern rebegin (busy_ap)");
|
||||
ramping = 1;
|
||||
}
|
||||
}
|
||||
ramped = TRUE;
|
||||
}
|
||||
|
||||
mps_message_discard(arena, msg);
|
||||
}
|
||||
|
||||
churn(ap, roots_count);
|
||||
{
|
||||
size_t r = (size_t)rnd();
|
||||
if (r % initTestFREQ == 0)
|
||||
*(int*)busy_init = -1; /* check that the buffer is still there */
|
||||
}
|
||||
if (objs % 1024 == 0) {
|
||||
putchar('.');
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
(void)mps_commit(busy_ap, busy_init, 64);
|
||||
mps_ap_destroy(busy_ap);
|
||||
mps_ap_destroy(ap);
|
||||
|
||||
for (i = 0; i < NELEMS(kids); ++i)
|
||||
testthr_join(&kids[i], NULL);
|
||||
}
|
||||
|
||||
static void test_arena(void)
|
||||
{
|
||||
size_t i;
|
||||
mps_fmt_t format;
|
||||
mps_chain_t chain;
|
||||
mps_thr_t thread;
|
||||
mps_root_t reg_root;
|
||||
mps_pool_t amc_pool, amcz_pool;
|
||||
void *marker = ▮
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
|
||||
} MPS_ARGS_END(args);
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
mps_message_type_enable(arena, mps_message_type_gc_start());
|
||||
|
||||
die(dylan_fmt(&format, arena), "fmt_create");
|
||||
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
|
||||
|
||||
for(i = 0; i < exactRootsCOUNT; ++i)
|
||||
exactRoots[i] = objNULL;
|
||||
for(i = 0; i < ambigRootsCOUNT; ++i)
|
||||
ambigRoots[i] = rnd_addr();
|
||||
|
||||
die(mps_root_create_table_masked(&exactRoot, arena,
|
||||
mps_rank_exact(), (mps_rm_t)0,
|
||||
&exactRoots[0], exactRootsCOUNT,
|
||||
(mps_word_t)1),
|
||||
"root_create_table(exact)");
|
||||
die(mps_root_create_table(&ambigRoot, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&ambigRoots[0], ambigRootsCOUNT),
|
||||
"root_create_table(ambig)");
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
die(mps_root_create_thread(®_root, arena, thread, marker),
|
||||
"root_create");
|
||||
|
||||
die(mps_pool_create(&amc_pool, arena, mps_class_amc(), format, chain),
|
||||
"pool_create(amc)");
|
||||
die(mps_pool_create(&amcz_pool, arena, mps_class_amcz(), format, chain),
|
||||
"pool_create(amcz)");
|
||||
|
||||
test_pool("AMC", amc_pool, exactRootsCOUNT);
|
||||
test_pool("AMCZ", amcz_pool, 0);
|
||||
|
||||
mps_arena_park(arena);
|
||||
mps_pool_destroy(amc_pool);
|
||||
mps_pool_destroy(amcz_pool);
|
||||
mps_root_destroy(reg_root);
|
||||
mps_thread_dereg(thread);
|
||||
mps_root_destroy(exactRoot);
|
||||
mps_root_destroy(ambigRoot);
|
||||
mps_chain_destroy(chain);
|
||||
mps_fmt_destroy(format);
|
||||
mps_arena_destroy(arena);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
testlib_init(argc, argv);
|
||||
test_arena();
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
275
mps/code/amsss.c
Normal file
275
mps/code/amsss.c
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
/* amsss.c: POOL CLASS AMS STRESS TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (c) 2002 Global Graphics Software.
|
||||
*
|
||||
* .design: Adapted from amcss.c, but not counting collections, just
|
||||
* total size of objects allocated (because epoch doesn't increment when
|
||||
* AMS is collected). */
|
||||
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpscams.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpstd.h"
|
||||
#include "mps.h"
|
||||
#include "mpm.h"
|
||||
|
||||
#include <stdio.h> /* fflush, printf */
|
||||
|
||||
|
||||
#define exactRootsCOUNT 50
|
||||
#define ambigRootsCOUNT 100
|
||||
/* This is enough for three GCs. */
|
||||
#define totalSizeMAX 800 * (size_t)1024
|
||||
#define totalSizeSTEP 200 * (size_t)1024
|
||||
/* objNULL needs to be odd so that it's ignored in exactRoots. */
|
||||
#define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED))
|
||||
#define testArenaSIZE ((size_t)1<<20)
|
||||
#define initTestFREQ 3000
|
||||
#define splatTestFREQ 6000
|
||||
static mps_gen_param_s testChain[1] = { { 160, 0.90 } };
|
||||
|
||||
|
||||
static mps_arena_t arena;
|
||||
static mps_ap_t ap;
|
||||
static mps_addr_t exactRoots[exactRootsCOUNT];
|
||||
static mps_addr_t ambigRoots[ambigRootsCOUNT];
|
||||
static size_t totalSize = 0;
|
||||
|
||||
|
||||
/* report - report statistics from any messages */
|
||||
|
||||
static void report(void)
|
||||
{
|
||||
static int nStart = 0;
|
||||
static int nComplete = 0;
|
||||
mps_message_type_t type;
|
||||
|
||||
while(mps_message_queue_type(&type, arena)) {
|
||||
mps_message_t message;
|
||||
|
||||
cdie(mps_message_get(&message, arena, type), "message get");
|
||||
|
||||
if (type == mps_message_type_gc_start()) {
|
||||
printf("\nCollection start %d. Because:\n", ++nStart);
|
||||
printf("%s\n", mps_message_gc_start_why(arena, message));
|
||||
|
||||
} else if (type == mps_message_type_gc()) {
|
||||
size_t live, condemned, not_condemned;
|
||||
|
||||
live = mps_message_gc_live_size(arena, message);
|
||||
condemned = mps_message_gc_condemned_size(arena, message);
|
||||
not_condemned = mps_message_gc_not_condemned_size(arena, message);
|
||||
|
||||
printf("\nCollection complete %d:\n", ++nComplete);
|
||||
printf("live %"PRIuLONGEST"\n", (ulongest_t)live);
|
||||
printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned);
|
||||
printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned);
|
||||
|
||||
} else {
|
||||
cdie(0, "unknown message type");
|
||||
}
|
||||
|
||||
mps_message_discard(arena, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* make -- object allocation and init */
|
||||
|
||||
static mps_addr_t make(void)
|
||||
{
|
||||
size_t length = rnd() % 20, size = (length+2) * sizeof(mps_word_t);
|
||||
mps_addr_t p;
|
||||
mps_res_t res;
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, p, ap, size);
|
||||
if (res)
|
||||
die(res, "MPS_RESERVE_BLOCK");
|
||||
res = dylan_init(p, size, exactRoots, exactRootsCOUNT);
|
||||
if (res)
|
||||
die(res, "dylan_init");
|
||||
} while(!mps_commit(ap, p, size));
|
||||
|
||||
totalSize += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* test -- the actual stress test */
|
||||
|
||||
static mps_pool_debug_option_s freecheckOptions =
|
||||
{ NULL, 0, "Dead", 4 };
|
||||
|
||||
static void test_pool(mps_pool_class_t pool_class, mps_arg_s args[],
|
||||
mps_bool_t haveAmbiguous)
|
||||
{
|
||||
mps_pool_t pool;
|
||||
mps_root_t exactRoot, ambigRoot = NULL;
|
||||
size_t lastStep = 0, i, r;
|
||||
unsigned long objs;
|
||||
mps_ap_t busy_ap;
|
||||
mps_addr_t busy_init;
|
||||
|
||||
die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create");
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
|
||||
die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2");
|
||||
|
||||
for(i = 0; i < exactRootsCOUNT; ++i)
|
||||
exactRoots[i] = objNULL;
|
||||
if (haveAmbiguous)
|
||||
for(i = 0; i < ambigRootsCOUNT; ++i)
|
||||
ambigRoots[i] = rnd_addr();
|
||||
|
||||
die(mps_root_create_table_masked(&exactRoot, arena,
|
||||
mps_rank_exact(), (mps_rm_t)0,
|
||||
&exactRoots[0], exactRootsCOUNT,
|
||||
(mps_word_t)1),
|
||||
"root_create_table(exact)");
|
||||
if (haveAmbiguous)
|
||||
die(mps_root_create_table(&ambigRoot, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&ambigRoots[0], ambigRootsCOUNT),
|
||||
"root_create_table(ambig)");
|
||||
|
||||
/* create an ap, and leave it busy */
|
||||
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
|
||||
|
||||
die(PoolDescribe(pool, mps_lib_get_stdout(), 0), "PoolDescribe");
|
||||
|
||||
objs = 0; totalSize = 0;
|
||||
while(totalSize < totalSizeMAX) {
|
||||
if (totalSize > lastStep + totalSizeSTEP) {
|
||||
lastStep = totalSize;
|
||||
printf("\nSize %"PRIuLONGEST" bytes, %lu objects.\n",
|
||||
(ulongest_t)totalSize, objs);
|
||||
(void)fflush(stdout);
|
||||
for(i = 0; i < exactRootsCOUNT; ++i)
|
||||
cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]),
|
||||
"all roots check");
|
||||
}
|
||||
|
||||
r = (size_t)rnd();
|
||||
if (!haveAmbiguous || (r & 1)) {
|
||||
i = (r >> 1) % exactRootsCOUNT;
|
||||
if (exactRoots[i] != objNULL)
|
||||
cdie(dylan_check(exactRoots[i]), "dying root check");
|
||||
exactRoots[i] = make();
|
||||
if (exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
|
||||
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
|
||||
exactRoots, exactRootsCOUNT);
|
||||
} else {
|
||||
i = (r >> 1) % ambigRootsCOUNT;
|
||||
ambigRoots[(ambigRootsCOUNT-1) - i] = make();
|
||||
/* Create random interior pointers */
|
||||
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
|
||||
}
|
||||
|
||||
if (rnd() % initTestFREQ == 0)
|
||||
*(int*)busy_init = -1; /* check that the buffer is still there */
|
||||
|
||||
if (rnd() % splatTestFREQ == 0)
|
||||
mps_pool_check_free_space(pool);
|
||||
|
||||
++objs;
|
||||
if (objs % 256 == 0) {
|
||||
printf(".");
|
||||
report();
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
(void)mps_commit(busy_ap, busy_init, 64);
|
||||
mps_ap_destroy(busy_ap);
|
||||
mps_ap_destroy(ap);
|
||||
mps_root_destroy(exactRoot);
|
||||
if (haveAmbiguous)
|
||||
mps_root_destroy(ambigRoot);
|
||||
|
||||
mps_pool_destroy(pool);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
mps_thr_t thread;
|
||||
mps_fmt_t format;
|
||||
mps_chain_t chain;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
mps_message_type_enable(arena, mps_message_type_gc_start());
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
die(mps_fmt_create_A(&format, arena, dylan_fmt_A()), "fmt_create");
|
||||
die(mps_chain_create(&chain, arena, 1, testChain), "chain_create");
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
int debug = i % 2;
|
||||
int ownChain = (i / 2) % 2;
|
||||
int ambig = (i / 4) % 2;
|
||||
printf("\n\n*** AMS%s with %sCHAIN and %sSUPPORT_AMBIGUOUS\n",
|
||||
debug ? " Debug" : "",
|
||||
ownChain ? "" : "!",
|
||||
ambig ? "" : "!");
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
|
||||
if (ownChain)
|
||||
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, ambig);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions);
|
||||
test_pool(debug ? mps_class_ams_debug() : mps_class_ams(), args, ambig);
|
||||
} MPS_ARGS_END(args);
|
||||
}
|
||||
|
||||
mps_arena_park(arena);
|
||||
mps_chain_destroy(chain);
|
||||
mps_fmt_destroy(format);
|
||||
mps_thread_dereg(thread);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
199
mps/code/amssshe.c
Normal file
199
mps/code/amssshe.c
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/* amssshe.c: POOL CLASS AMS STRESS TEST WITH HEADERS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .design: Adapted from amsss.c.
|
||||
*/
|
||||
|
||||
#include "fmthe.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpscams.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpstd.h"
|
||||
#include "mps.h"
|
||||
|
||||
#include <stdio.h> /* fflush, printf */
|
||||
|
||||
|
||||
#define exactRootsCOUNT 50
|
||||
#define ambigRootsCOUNT 100
|
||||
/* This is enough for five GCs. */
|
||||
#define totalSizeMAX 800 * (size_t)1024
|
||||
#define totalSizeSTEP 200 * (size_t)1024
|
||||
/* objNULL needs to be odd so that it's ignored in exactRoots. */
|
||||
#define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED))
|
||||
#define testArenaSIZE ((size_t)16<<20)
|
||||
#define initTestFREQ 6000
|
||||
static mps_gen_param_s testChain[1] = { { 160, 0.90 } };
|
||||
|
||||
|
||||
static mps_pool_t pool;
|
||||
static mps_ap_t ap;
|
||||
static mps_addr_t exactRoots[exactRootsCOUNT];
|
||||
static mps_addr_t ambigRoots[ambigRootsCOUNT];
|
||||
static size_t totalSize = 0;
|
||||
|
||||
|
||||
static mps_addr_t make(void)
|
||||
{
|
||||
size_t length = rnd() % 20, size = (length+2) * sizeof(mps_word_t);
|
||||
mps_addr_t p, userP;
|
||||
mps_res_t res;
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, p, ap, size + headerSIZE);
|
||||
if(res)
|
||||
die(res, "MPS_RESERVE_BLOCK");
|
||||
userP = (mps_addr_t)((char*)p + headerSIZE);
|
||||
res = dylan_init(userP, size, exactRoots, exactRootsCOUNT);
|
||||
if(res)
|
||||
die(res, "dylan_init");
|
||||
((int*)p)[0] = realHeader;
|
||||
((int*)p)[1] = 0xED0ED;
|
||||
} while(!mps_commit(ap, p, size + headerSIZE));
|
||||
|
||||
totalSize += size;
|
||||
return userP;
|
||||
}
|
||||
|
||||
|
||||
static void test(mps_arena_t arena)
|
||||
{
|
||||
mps_fmt_t format;
|
||||
mps_chain_t chain;
|
||||
mps_root_t exactRoot, ambigRoot;
|
||||
size_t lastStep = 0, i, r;
|
||||
unsigned long objs;
|
||||
mps_ap_t busy_ap;
|
||||
mps_addr_t busy_init;
|
||||
|
||||
die(EnsureHeaderFormat(&format, arena), "make header format");
|
||||
die(mps_chain_create(&chain, arena, 1, testChain), "chain_create");
|
||||
die(mps_pool_create(&pool, arena, mps_class_ams(), format, chain,
|
||||
TRUE), "pool_create(ams)");
|
||||
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
|
||||
die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2");
|
||||
|
||||
for(i = 0; i < exactRootsCOUNT; ++i)
|
||||
exactRoots[i] = objNULL;
|
||||
for(i = 0; i < ambigRootsCOUNT; ++i)
|
||||
ambigRoots[i] = rnd_addr();
|
||||
|
||||
die(mps_root_create_table_masked(&exactRoot, arena,
|
||||
mps_rank_exact(), (mps_rm_t)0,
|
||||
&exactRoots[0], exactRootsCOUNT,
|
||||
(mps_word_t)1),
|
||||
"root_create_table(exact)");
|
||||
die(mps_root_create_table(&ambigRoot, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&ambigRoots[0], ambigRootsCOUNT),
|
||||
"root_create_table(ambig)");
|
||||
|
||||
/* create an ap, and leave it busy */
|
||||
die(mps_reserve(&busy_init, busy_ap, 64), "mps_reserve busy");
|
||||
|
||||
objs = 0;
|
||||
while(totalSize < totalSizeMAX) {
|
||||
if(totalSize > lastStep + totalSizeSTEP) {
|
||||
lastStep = totalSize;
|
||||
printf("\nSize %"PRIuLONGEST" bytes, %lu objects.\n",
|
||||
(ulongest_t)totalSize, objs);
|
||||
(void)fflush(stdout);
|
||||
for(i = 0; i < exactRootsCOUNT; ++i)
|
||||
cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]),
|
||||
"all roots check");
|
||||
}
|
||||
|
||||
r = (size_t)rnd();
|
||||
if(r & 1) {
|
||||
i = (r >> 1) % exactRootsCOUNT;
|
||||
if(exactRoots[i] != objNULL)
|
||||
cdie(dylan_check(exactRoots[i]), "dying root check");
|
||||
exactRoots[i] = make();
|
||||
if(exactRoots[(exactRootsCOUNT-1) - i] != objNULL)
|
||||
dylan_write(exactRoots[(exactRootsCOUNT-1) - i],
|
||||
exactRoots, exactRootsCOUNT);
|
||||
} else {
|
||||
i = (r >> 1) % ambigRootsCOUNT;
|
||||
ambigRoots[(ambigRootsCOUNT-1) - i] = make();
|
||||
/* Create random interior pointers */
|
||||
ambigRoots[i] = (mps_addr_t)((char *)(ambigRoots[i/2]) + 1);
|
||||
}
|
||||
|
||||
if(rnd() % initTestFREQ == 0)
|
||||
*(int*)busy_init = -1; /* check that the buffer is still there */
|
||||
|
||||
++objs;
|
||||
if (objs % 256 == 0) {
|
||||
printf(".");
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
(void)mps_commit(busy_ap, busy_init, 64);
|
||||
mps_arena_park(arena);
|
||||
mps_ap_destroy(busy_ap);
|
||||
mps_ap_destroy(ap);
|
||||
mps_root_destroy(exactRoot);
|
||||
mps_root_destroy(ambigRoot);
|
||||
mps_pool_destroy(pool);
|
||||
mps_chain_destroy(chain);
|
||||
mps_fmt_destroy(format);
|
||||
mps_arena_release(arena);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thread;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE));
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create");
|
||||
} MPS_ARGS_END(args);
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
test(arena);
|
||||
mps_thread_dereg(thread);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
55
mps/code/anangc.gmk
Normal file
55
mps/code/anangc.gmk
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# anangc.gmk: BUILD FOR ANSI/ANSI/GCC PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = anangc
|
||||
|
||||
MPMPF = \
|
||||
lockan.c \
|
||||
prmcan.c \
|
||||
prmcanan.c \
|
||||
protan.c \
|
||||
span.c \
|
||||
than.c \
|
||||
vman.c
|
||||
|
||||
LIBS = -lm -lpthread
|
||||
|
||||
include gc.gmk
|
||||
|
||||
CFLAGSCOMPILER += -DCONFIG_PF_ANSI -DCONFIG_THREAD_SINGLE
|
||||
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
56
mps/code/ananll.gmk
Normal file
56
mps/code/ananll.gmk
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# ananll.gmk: BUILD FOR ANSI/ANSI/Clang PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2014-2020 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = ananll
|
||||
|
||||
MPMPF = \
|
||||
lockan.c \
|
||||
prmcan.c \
|
||||
prmcanan.c \
|
||||
protan.c \
|
||||
span.c \
|
||||
than.c \
|
||||
vman.c
|
||||
|
||||
LIBS = -lm -lpthread
|
||||
|
||||
include ll.gmk
|
||||
|
||||
CFLAGSCOMPILER += -DCONFIG_PF_ANSI -DCONFIG_THREAD_SINGLE
|
||||
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
51
mps/code/ananmv.nmk
Normal file
51
mps/code/ananmv.nmk
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# ananmv.nmk: ANSI/ANSI/MICROSOFT VISUAL C/C++ NMAKE FILE -*- makefile -*-
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = ananmv
|
||||
|
||||
PFMDEFS = /DCONFIG_PF_ANSI /DCONFIG_THREAD_SINGLE
|
||||
|
||||
MPMPF = \
|
||||
[lockan] \
|
||||
[prmcan] \
|
||||
[prmcanan] \
|
||||
[protan] \
|
||||
[span] \
|
||||
[than] \
|
||||
[vman]
|
||||
|
||||
!INCLUDE commpre.nmk
|
||||
!INCLUDE mv.nmk
|
||||
!INCLUDE commpost.nmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
274
mps/code/apss.c
Normal file
274
mps/code/apss.c
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/* apss.c: AP MANUAL ALLOC STRESS TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*/
|
||||
|
||||
|
||||
#include "mpscmvff.h"
|
||||
#include "mpscmvt.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpsacl.h"
|
||||
#include "mpsavm.h"
|
||||
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
#include <stdlib.h> /* malloc */
|
||||
|
||||
|
||||
#define testArenaSIZE ((((size_t)3)<<24) - 4)
|
||||
#define testSetSIZE 200
|
||||
#define testLOOPS 10
|
||||
#define MAX_ALIGN 64 /* TODO: Make this test work up to arena_grain_size? */
|
||||
|
||||
|
||||
/* make -- allocate one object */
|
||||
|
||||
static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size)
|
||||
{
|
||||
mps_res_t res;
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, *p, ap, size);
|
||||
if(res != MPS_RES_OK)
|
||||
return res;
|
||||
} while(!mps_commit(ap, *p, size));
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
/* check_allocated_size -- check the allocated size of the pool */
|
||||
|
||||
static void check_allocated_size(mps_pool_t pool, mps_ap_t ap, size_t allocated)
|
||||
{
|
||||
size_t total_size = mps_pool_total_size(pool);
|
||||
size_t free_size = mps_pool_free_size(pool);
|
||||
size_t ap_free = (size_t)((char *)ap->limit - (char *)ap->init);
|
||||
Insist(total_size - free_size == allocated + ap_free);
|
||||
}
|
||||
|
||||
|
||||
/* stress -- create a pool of the requested type and allocate in it */
|
||||
|
||||
static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options,
|
||||
mps_align_t align,
|
||||
size_t (*size)(size_t i, mps_align_t align),
|
||||
const char *name, mps_pool_class_t pool_class,
|
||||
mps_arg_s args[])
|
||||
{
|
||||
mps_res_t res = MPS_RES_OK;
|
||||
mps_pool_t pool;
|
||||
mps_ap_t ap;
|
||||
size_t i, k;
|
||||
int *ps[testSetSIZE];
|
||||
size_t ss[testSetSIZE];
|
||||
size_t allocated = 0; /* Total allocated memory */
|
||||
size_t debugOverhead = options ? 2 * alignUp(options->fence_size, align) : 0;
|
||||
|
||||
printf("stress %s\n", name);
|
||||
|
||||
die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create");
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
|
||||
|
||||
/* allocate a load of objects */
|
||||
for (i=0; i<testSetSIZE; ++i) {
|
||||
mps_addr_t obj;
|
||||
ss[i] = (*size)(i, align);
|
||||
res = make(&obj, ap, ss[i]);
|
||||
if (res != MPS_RES_OK)
|
||||
goto allocFail;
|
||||
ps[i] = obj;
|
||||
allocated += ss[i] + debugOverhead;
|
||||
if (ss[i] >= sizeof(ps[i]))
|
||||
*ps[i] = 1; /* Write something, so it gets swap. */
|
||||
check_allocated_size(pool, ap, allocated);
|
||||
}
|
||||
|
||||
/* Check introspection functions */
|
||||
for (i = 0; i < NELEMS(ps); ++i) {
|
||||
mps_pool_t addr_pool = NULL;
|
||||
Insist(mps_arena_has_addr(arena, ps[i]));
|
||||
Insist(mps_addr_pool(&addr_pool, arena, ps[i]));
|
||||
Insist(addr_pool == pool);
|
||||
}
|
||||
|
||||
mps_pool_check_fenceposts(pool);
|
||||
|
||||
for (k=0; k<testLOOPS; ++k) {
|
||||
/* shuffle all the objects */
|
||||
for (i=0; i<testSetSIZE; ++i) {
|
||||
size_t j = rnd()%(testSetSIZE-i);
|
||||
void *tp;
|
||||
size_t ts;
|
||||
|
||||
tp = ps[j]; ts = ss[j];
|
||||
ps[j] = ps[i]; ss[j] = ss[i];
|
||||
ps[i] = tp; ss[i] = ts;
|
||||
}
|
||||
/* free half of the objects */
|
||||
/* upper half, as when allocating them again we want smaller objects */
|
||||
/* see randomSize() */
|
||||
for (i=testSetSIZE/2; i<testSetSIZE; ++i) {
|
||||
mps_free(pool, (mps_addr_t)ps[i], ss[i]);
|
||||
/* if (i == testSetSIZE/2) */
|
||||
/* PoolDescribe((Pool)pool, mps_lib_stdout); */
|
||||
Insist(ss[i] + debugOverhead <= allocated);
|
||||
allocated -= ss[i] + debugOverhead;
|
||||
}
|
||||
/* allocate some new objects */
|
||||
for (i=testSetSIZE/2; i<testSetSIZE; ++i) {
|
||||
mps_addr_t obj;
|
||||
ss[i] = (*size)(i, align);
|
||||
res = make(&obj, ap, ss[i]);
|
||||
if (res != MPS_RES_OK)
|
||||
goto allocFail;
|
||||
ps[i] = obj;
|
||||
allocated += ss[i] + debugOverhead;
|
||||
}
|
||||
check_allocated_size(pool, ap, allocated);
|
||||
}
|
||||
|
||||
allocFail:
|
||||
mps_ap_destroy(ap);
|
||||
mps_pool_destroy(pool);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* randomSizeAligned -- produce sizes both large and small, aligned to
|
||||
* align.
|
||||
*/
|
||||
|
||||
static size_t randomSizeAligned(size_t i, mps_align_t align)
|
||||
{
|
||||
size_t maxSize = 2 * 160 * 0x2000;
|
||||
/* Reduce by a factor of 2 every 10 cycles. Total allocation about 40 MB. */
|
||||
return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, align);
|
||||
}
|
||||
|
||||
|
||||
static mps_pool_debug_option_s bothOptions = {
|
||||
/* .fence_template = */ "post",
|
||||
/* .fence_size = */ 4,
|
||||
/* .free_template = */ "DEAD",
|
||||
/* .free_size = */ 4
|
||||
};
|
||||
|
||||
static mps_pool_debug_option_s fenceOptions = {
|
||||
/* .fence_template = */ "123456789abcdef",
|
||||
/* .fence_size = */ 15,
|
||||
/* .free_template = */ NULL,
|
||||
/* .free_size = */ 0
|
||||
};
|
||||
|
||||
|
||||
/* test -- create arena using given class and arguments; test all the
|
||||
* pool classes in this arena
|
||||
*/
|
||||
|
||||
static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[],
|
||||
size_t arena_grain_size,
|
||||
mps_pool_debug_option_s *options)
|
||||
{
|
||||
mps_arena_t arena;
|
||||
die(mps_arena_create_k(&arena, arena_class, arena_args), "mps_arena_create");
|
||||
|
||||
(void)arena_grain_size; /* TODO: test larger alignments up to this */
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
mps_align_t align = rnd_align(sizeof(void *), MAX_ALIGN);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, TRUE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, TRUE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, TRUE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_SPARE, rnd_double());
|
||||
die(stress(arena, NULL, align, randomSizeAligned, "MVFF",
|
||||
mps_class_mvff(), args), "stress MVFF");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
/* IWBN to test MVFFDebug, but the MPS doesn't support debugging
|
||||
allocation points. See job003995. */
|
||||
(void)options;
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
mps_align_t align = rnd_align(sizeof(void *), MAX_ALIGN);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align);
|
||||
die(stress(arena, NULL, align, randomSizeAligned, "MVT",
|
||||
mps_class_mvt(), args), "stress MVT");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
/* Manual allocation should not cause any garbage collections. */
|
||||
Insist(mps_collections(arena) == 0);
|
||||
mps_arena_destroy(arena);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
size_t arena_grain_size;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
arena_grain_size = rnd_grain(testArenaSIZE);
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, testArenaSIZE);
|
||||
test(mps_arena_class_vm(), args, arena_grain_size, &fenceOptions);
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
arena_grain_size = rnd_grain(2 * testArenaSIZE);
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size);
|
||||
test(mps_arena_class_vm(), args, arena_grain_size, &bothOptions);
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
arena_grain_size = rnd_grain(testArenaSIZE);
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, malloc(testArenaSIZE));
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size);
|
||||
test(mps_arena_class_cl(), args, arena_grain_size, &bothOptions);
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
1411
mps/code/arena.c
Normal file
1411
mps/code/arena.c
Normal file
File diff suppressed because it is too large
Load diff
502
mps/code/arenacl.c
Normal file
502
mps/code/arenacl.c
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
/* arenacl.c: ARENA CLASS USING CLIENT MEMORY
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .design: <design/arena#.client>.
|
||||
*
|
||||
* .improve.remember: One possible performance improvement is to
|
||||
* remember (a conservative approximation to) the indices of the first
|
||||
* and last free pages in each chunk, and start searching from these
|
||||
* in ChunkAlloc. See request.epcore.170534_.
|
||||
*
|
||||
* .. _request.epcore.170534: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/epcore/170534
|
||||
*/
|
||||
|
||||
#include "boot.h"
|
||||
#include "tract.h"
|
||||
#include "bt.h"
|
||||
#include "mpm.h"
|
||||
#include "mpsacl.h"
|
||||
|
||||
SRCID(arenacl, "$Id$");
|
||||
|
||||
DECLARE_CLASS(Arena, ClientArena, AbstractArena);
|
||||
|
||||
|
||||
/* ClientArenaStruct -- Client Arena Structure */
|
||||
|
||||
#define ClientArenaSig ((Sig)0x519A6EC7) /* SIGnature AREna CLient */
|
||||
|
||||
typedef struct ClientArenaStruct {
|
||||
ArenaStruct arenaStruct; /* generic arena structure */
|
||||
Sig sig; /* design.mps.sig.field.end.outer */
|
||||
} ClientArenaStruct;
|
||||
typedef struct ClientArenaStruct *ClientArena;
|
||||
|
||||
|
||||
/* CLChunk -- chunk structure */
|
||||
|
||||
typedef struct ClientChunkStruct *ClientChunk;
|
||||
|
||||
#define ClientChunkSig ((Sig)0x519A6C2C) /* SIGnature ARena CLient Chunk */
|
||||
|
||||
typedef struct ClientChunkStruct {
|
||||
ChunkStruct chunkStruct; /* generic chunk */
|
||||
Size freePages; /* number of free pages in chunk */
|
||||
Addr pageBase; /* base of first managed page in chunk */
|
||||
Sig sig; /* design.mps.sig.field.end.outer */
|
||||
} ClientChunkStruct;
|
||||
|
||||
#define ClientChunk2Chunk(clchunk) (&(clchunk)->chunkStruct)
|
||||
#define Chunk2ClientChunk(chunk) PARENT(ClientChunkStruct, chunkStruct, chunk)
|
||||
|
||||
|
||||
/* ClientChunkClientArena -- get the client arena from a client chunk */
|
||||
|
||||
#define ClientChunkClientArena(clchunk) \
|
||||
Arena2ClientArena(ChunkArena(ClientChunk2Chunk(clchunk)))
|
||||
|
||||
|
||||
/* ClientChunkCheck -- check the consistency of a client chunk */
|
||||
|
||||
ATTRIBUTE_UNUSED
|
||||
static Bool ClientChunkCheck(ClientChunk clChunk)
|
||||
{
|
||||
Chunk chunk;
|
||||
|
||||
CHECKS(ClientChunk, clChunk);
|
||||
chunk = ClientChunk2Chunk(clChunk);
|
||||
CHECKD(Chunk, chunk);
|
||||
CHECKL(clChunk->freePages <= chunk->pages);
|
||||
/* check they don't overlap (knowing the order) */
|
||||
CHECKL((Addr)(chunk + 1) < (Addr)chunk->allocTable);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaCheck -- check the consistency of a client arena */
|
||||
|
||||
ATTRIBUTE_UNUSED
|
||||
static Bool ClientArenaCheck(ClientArena clientArena)
|
||||
{
|
||||
Arena arena = MustBeA(AbstractArena, clientArena);
|
||||
|
||||
/* See <code/arena.c#.reserved.check> */
|
||||
CHECKL(arena->committed <= arena->reserved);
|
||||
CHECKL(arena->spareCommitted == 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* clientChunkCreate -- create a ClientChunk */
|
||||
|
||||
static Res clientChunkCreate(Chunk *chunkReturn, ClientArena clientArena,
|
||||
Addr base, Addr limit)
|
||||
{
|
||||
Arena arena = MustBeA(AbstractArena, clientArena);
|
||||
ClientChunk clChunk;
|
||||
Chunk chunk;
|
||||
Addr alignedBase;
|
||||
BootBlockStruct bootStruct;
|
||||
BootBlock boot = &bootStruct;
|
||||
Res res;
|
||||
void *p;
|
||||
|
||||
AVER(chunkReturn != NULL);
|
||||
AVER(base != (Addr)0);
|
||||
AVER(limit != (Addr)0);
|
||||
AVER(limit > base);
|
||||
|
||||
/* Initialize boot block. */
|
||||
/* Chunk has to be page-aligned, and the boot allocs must be within it. */
|
||||
alignedBase = AddrAlignUp(base, ArenaGrainSize(arena));
|
||||
AVER(alignedBase < limit);
|
||||
res = BootBlockInit(boot, (void *)alignedBase, (void *)limit);
|
||||
if (res != ResOK)
|
||||
goto failBootInit;
|
||||
|
||||
/* Allocate the chunk. */
|
||||
/* TODO: Add reference to design. */
|
||||
res = BootAlloc(&p, boot, sizeof(ClientChunkStruct), MPS_PF_ALIGN);
|
||||
if (res != ResOK)
|
||||
goto failChunkAlloc;
|
||||
clChunk = p;
|
||||
chunk = ClientChunk2Chunk(clChunk);
|
||||
|
||||
res = ChunkInit(chunk, arena, alignedBase,
|
||||
AddrAlignDown(limit, ArenaGrainSize(arena)),
|
||||
AddrOffset(base, limit), boot);
|
||||
if (res != ResOK)
|
||||
goto failChunkInit;
|
||||
|
||||
arena->committed += ChunkPagesToSize(chunk, chunk->allocBase);
|
||||
|
||||
BootBlockFinish(boot);
|
||||
|
||||
clChunk->sig = ClientChunkSig;
|
||||
AVERT(ClientChunk, clChunk);
|
||||
*chunkReturn = chunk;
|
||||
return ResOK;
|
||||
|
||||
failChunkInit:
|
||||
failChunkAlloc:
|
||||
failBootInit:
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* ClientChunkInit -- initialize a ClientChunk */
|
||||
|
||||
static Res ClientChunkInit(Chunk chunk, BootBlock boot)
|
||||
{
|
||||
Res res;
|
||||
ClientChunk clChunk;
|
||||
void *p;
|
||||
|
||||
/* chunk is supposed to be uninitialized, so don't check it. */
|
||||
clChunk = Chunk2ClientChunk(chunk);
|
||||
AVERT(BootBlock, boot);
|
||||
|
||||
/* TODO: An old comment claimed this is too large.
|
||||
Does it fail to exclude the page table or something? */
|
||||
clChunk->freePages = chunk->pages;
|
||||
|
||||
/* Put the page table as late as possible, as in VM systems we don't want */
|
||||
/* to map it. */
|
||||
res = BootAlloc(&p, boot, chunk->pageTablePages << chunk->pageShift, chunk->pageSize);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
chunk->pageTable = p;
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* clientChunkDestroy -- destroy a ClientChunk */
|
||||
|
||||
static Bool clientChunkDestroy(Tree tree, void *closure)
|
||||
{
|
||||
Arena arena;
|
||||
Chunk chunk;
|
||||
ClientChunk clChunk;
|
||||
Size size;
|
||||
|
||||
AVERT(Tree, tree);
|
||||
AVER(closure == UNUSED_POINTER);
|
||||
UNUSED(closure);
|
||||
|
||||
chunk = ChunkOfTree(tree);
|
||||
AVERT(Chunk, chunk);
|
||||
arena = ChunkArena(chunk);
|
||||
AVERT(Arena, arena);
|
||||
clChunk = Chunk2ClientChunk(chunk);
|
||||
AVERT(ClientChunk, clChunk);
|
||||
AVER(chunk->pages == clChunk->freePages);
|
||||
|
||||
size = ChunkPagesToSize(chunk, chunk->allocBase);
|
||||
AVER(arena->committed >= size);
|
||||
arena->committed -= size;
|
||||
|
||||
clChunk->sig = SigInvalid;
|
||||
ChunkFinish(chunk);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ClientChunkFinish -- finish a ClientChunk */
|
||||
|
||||
static void ClientChunkFinish(Chunk chunk)
|
||||
{
|
||||
/* Can't check chunk as it's not valid anymore. */
|
||||
UNUSED(chunk);
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaVarargs -- parse obsolete varargs */
|
||||
|
||||
static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
|
||||
{
|
||||
args[0].key = MPS_KEY_ARENA_SIZE;
|
||||
args[0].val.size = va_arg(varargs, Size);
|
||||
args[1].key = MPS_KEY_ARENA_CL_BASE;
|
||||
args[1].val.addr = va_arg(varargs, Addr);
|
||||
args[2].key = MPS_KEY_ARGS_END;
|
||||
AVERT(ArgList, args);
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaInit -- create and initialize the client arena
|
||||
*
|
||||
* .init.memory: Creates the arena structure in the chuck given, and
|
||||
* makes the first chunk from the memory left over.
|
||||
* .arena.init: Once the arena has been allocated, we call ArenaInit
|
||||
* to do the generic part of init.
|
||||
*/
|
||||
|
||||
ARG_DEFINE_KEY(ARENA_CL_BASE, Addr);
|
||||
|
||||
static Res ClientArenaCreate(Arena *arenaReturn, ArgList args)
|
||||
{
|
||||
Arena arena;
|
||||
ClientArena clientArena;
|
||||
Size size;
|
||||
Size clArenaSize; /* aligned size of ClientArenaStruct */
|
||||
Addr base, limit, chunkBase;
|
||||
Align grainSize = 1;
|
||||
Res res;
|
||||
Chunk chunk;
|
||||
mps_arg_s arg;
|
||||
|
||||
AVER(arenaReturn != NULL);
|
||||
AVERT(ArgList, args);
|
||||
|
||||
ArgRequire(&arg, args, MPS_KEY_ARENA_SIZE);
|
||||
size = arg.val.size;
|
||||
ArgRequire(&arg, args, MPS_KEY_ARENA_CL_BASE);
|
||||
base = arg.val.addr;
|
||||
if (ArgPick(&arg, args, MPS_KEY_ARENA_GRAIN_SIZE))
|
||||
grainSize = arg.val.size;
|
||||
grainSize = SizeAlignUp(grainSize, ARENA_CLIENT_GRAIN_SIZE);
|
||||
grainSize = SizeAlignUp(grainSize, ProtGranularity());
|
||||
|
||||
AVER(base != (Addr)0);
|
||||
AVERT(ArenaGrainSize, grainSize);
|
||||
|
||||
if (size < grainSize * MPS_WORD_WIDTH)
|
||||
/* Not enough room for a full complement of zones. */
|
||||
return ResMEMORY;
|
||||
|
||||
clArenaSize = SizeAlignUp(sizeof(ClientArenaStruct), MPS_PF_ALIGN);
|
||||
if (size < clArenaSize)
|
||||
return ResMEMORY;
|
||||
|
||||
limit = AddrAdd(base, size);
|
||||
|
||||
/* allocate the arena */
|
||||
base = AddrAlignUp(base, MPS_PF_ALIGN);
|
||||
clientArena = (ClientArena)base;
|
||||
chunkBase = AddrAlignUp(AddrAdd(base, clArenaSize), MPS_PF_ALIGN);
|
||||
if (chunkBase > limit)
|
||||
return ResMEMORY;
|
||||
|
||||
arena = CouldBeA(AbstractArena, clientArena);
|
||||
|
||||
res = NextMethod(Arena, ClientArena, init)(arena, grainSize, args);
|
||||
if (res != ResOK)
|
||||
goto failSuperInit;
|
||||
SetClassOfPoly(arena, CLASS(ClientArena));
|
||||
AVER(clientArena == MustBeA(ClientArena, arena));
|
||||
|
||||
/* have to have a valid arena before calling ChunkCreate */
|
||||
clientArena->sig = ClientArenaSig;
|
||||
|
||||
res = clientChunkCreate(&chunk, clientArena, chunkBase, limit);
|
||||
if (res != ResOK)
|
||||
goto failChunkCreate;
|
||||
arena->primary = chunk;
|
||||
|
||||
/* Set the zone shift to divide the initial chunk into the same */
|
||||
/* number of zones as will fit into a reference set (the number of */
|
||||
/* bits in a word). Note that some zones are discontiguous in the */
|
||||
/* arena if the size is not a power of 2. */
|
||||
arena->zoneShift = SizeFloorLog2(size >> MPS_WORD_SHIFT);
|
||||
AVER(ArenaGrainSize(arena) == ChunkPageSize(arena->primary));
|
||||
|
||||
EVENT7(ArenaCreateCL, arena, size, base, grainSize,
|
||||
ClassOfPoly(Arena, arena), ArenaGlobals(arena)->systemPools,
|
||||
arena->serial);
|
||||
AVERT(ClientArena, clientArena);
|
||||
*arenaReturn = arena;
|
||||
return ResOK;
|
||||
|
||||
failChunkCreate:
|
||||
NextMethod(Inst, ClientArena, finish)(MustBeA(Inst, arena));
|
||||
failSuperInit:
|
||||
AVER(res != ResOK);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaDestroy -- destroy the arena */
|
||||
|
||||
static void ClientArenaDestroy(Arena arena)
|
||||
{
|
||||
ClientArena clientArena = MustBeA(ClientArena, arena);
|
||||
|
||||
/* Destroy all chunks, including the primary. See
|
||||
* <design/arena#.chunk.delete> */
|
||||
arena->primary = NULL;
|
||||
TreeTraverseAndDelete(&arena->chunkTree, clientChunkDestroy,
|
||||
UNUSED_POINTER);
|
||||
|
||||
clientArena->sig = SigInvalid;
|
||||
|
||||
/* Destroying the chunks should leave nothing behind. */
|
||||
AVER(arena->reserved == 0);
|
||||
AVER(arena->committed == 0);
|
||||
|
||||
NextMethod(Inst, ClientArena, finish)(MustBeA(Inst, arena));
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaExtend -- extend the arena */
|
||||
|
||||
static Res ClientArenaExtend(Arena arena, Addr base, Size size)
|
||||
{
|
||||
ClientArena clientArena = MustBeA(ClientArena, arena);
|
||||
Chunk chunk;
|
||||
|
||||
AVER(base != (Addr)0);
|
||||
AVER(size > 0);
|
||||
|
||||
return clientChunkCreate(&chunk, clientArena, base, AddrAdd(base, size));
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaPagesMarkAllocated -- Mark the pages allocated */
|
||||
|
||||
static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk,
|
||||
Index baseIndex, Count pages,
|
||||
Pool pool)
|
||||
{
|
||||
Index i;
|
||||
ClientChunk clChunk;
|
||||
|
||||
AVERT(Arena, arena);
|
||||
AVERT(Chunk, chunk);
|
||||
clChunk = Chunk2ClientChunk(chunk);
|
||||
AVERT(ClientChunk, clChunk);
|
||||
AVER(chunk->allocBase <= baseIndex);
|
||||
AVER(pages > 0);
|
||||
AVER(baseIndex + pages <= chunk->pages);
|
||||
AVERT(Pool, pool);
|
||||
|
||||
for (i = 0; i < pages; ++i)
|
||||
PageAlloc(chunk, baseIndex + i, pool);
|
||||
|
||||
arena->committed += ChunkPagesToSize(chunk, pages);
|
||||
AVER(clChunk->freePages >= pages);
|
||||
clChunk->freePages -= pages;
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* ClientChunkPageMapped -- determine if a page is mapped */
|
||||
|
||||
static Bool ClientChunkPageMapped(Chunk chunk, Index index)
|
||||
{
|
||||
UNUSED(chunk);
|
||||
UNUSED(index);
|
||||
|
||||
AVERT(Chunk, chunk);
|
||||
AVER(index < chunk->pages);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaFree - free a region in the arena */
|
||||
|
||||
static void ClientArenaFree(Addr base, Size size, Pool pool)
|
||||
{
|
||||
Arena arena;
|
||||
Chunk chunk = NULL; /* suppress "may be used uninitialized" */
|
||||
Size pages;
|
||||
Index pi, baseIndex, limitIndex;
|
||||
Bool foundChunk;
|
||||
ClientChunk clChunk;
|
||||
|
||||
AVER(base != NULL);
|
||||
AVER(size > (Size)0);
|
||||
AVERT(Pool, pool);
|
||||
arena = PoolArena(pool);
|
||||
AVERC(ClientArena, arena);
|
||||
AVER(SizeIsAligned(size, ChunkPageSize(arena->primary)));
|
||||
AVER(AddrIsAligned(base, ChunkPageSize(arena->primary)));
|
||||
|
||||
foundChunk = ChunkOfAddr(&chunk, arena, base);
|
||||
AVER(foundChunk);
|
||||
clChunk = Chunk2ClientChunk(chunk);
|
||||
AVERT(ClientChunk, clChunk);
|
||||
|
||||
pages = ChunkSizeToPages(chunk, size);
|
||||
baseIndex = INDEX_OF_ADDR(chunk, base);
|
||||
limitIndex = baseIndex + pages;
|
||||
AVER(baseIndex < limitIndex);
|
||||
AVER(limitIndex <= chunk->pages);
|
||||
|
||||
for(pi = baseIndex; pi < limitIndex; pi++) {
|
||||
Tract tract = PageTract(ChunkPage(chunk, pi));
|
||||
|
||||
AVER(TractPool(tract) == pool);
|
||||
TractFinish(tract);
|
||||
}
|
||||
|
||||
AVER(BTIsSetRange(chunk->allocTable, baseIndex, limitIndex));
|
||||
BTResRange(chunk->allocTable, baseIndex, limitIndex);
|
||||
|
||||
AVER(arena->committed >= size);
|
||||
arena->committed -= size;
|
||||
clChunk->freePages += pages;
|
||||
}
|
||||
|
||||
|
||||
/* ClientArenaClass -- The Client arena class definition */
|
||||
|
||||
DEFINE_CLASS(Arena, ClientArena, klass)
|
||||
{
|
||||
INHERIT_CLASS(klass, ClientArena, AbstractArena);
|
||||
klass->size = sizeof(ClientArenaStruct);
|
||||
klass->varargs = ClientArenaVarargs;
|
||||
klass->create = ClientArenaCreate;
|
||||
klass->destroy = ClientArenaDestroy;
|
||||
klass->extend = ClientArenaExtend;
|
||||
klass->pagesMarkAllocated = ClientArenaPagesMarkAllocated;
|
||||
klass->free = ClientArenaFree;
|
||||
klass->chunkInit = ClientChunkInit;
|
||||
klass->chunkFinish = ClientChunkFinish;
|
||||
klass->chunkPageMapped = ClientChunkPageMapped;
|
||||
AVERT(ArenaClass, klass);
|
||||
}
|
||||
|
||||
|
||||
/* mps_arena_class_cl -- return the arena class CL */
|
||||
|
||||
mps_arena_class_t mps_arena_class_cl(void)
|
||||
{
|
||||
return (mps_arena_class_t)CLASS(ClientArena);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
517
mps/code/arenacv.c
Normal file
517
mps/code/arenacv.c
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
/* arenacv.c: ARENA COVERAGE TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .coverage: At the moment, we're only trying to cover the new code
|
||||
* (partial mapping of the page table and vm overflow).
|
||||
*
|
||||
* .note.tract-size: If the page size is divisible by sizeof(TractStruct), many
|
||||
* test cases end up being essentially identical -- there just aren't that
|
||||
* many different cases then.
|
||||
*
|
||||
* .improve.gap-below: Could test different-sized gaps below the tract
|
||||
* being allocated; this requires using two adjacent zones.
|
||||
*/
|
||||
|
||||
#include "mpm.h"
|
||||
#include "poolmvff.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpsacl.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
#include <stdlib.h> /* malloc */
|
||||
|
||||
|
||||
#define tractsSIZE 500
|
||||
|
||||
|
||||
/* testAllocAndIterate -- Test arena allocation and iteration
|
||||
*
|
||||
* .tract-seg: Test allocation and iteration, using both low-level
|
||||
* tracts and higher-level segments. To do this, contrive a set of
|
||||
* allocation and iteration functions which are interchangeable.
|
||||
*/
|
||||
|
||||
/* Type definitions for the interchangability interface */
|
||||
|
||||
|
||||
/* AllocInfo -- interchangeable info about allocated regions */
|
||||
|
||||
typedef struct AllocInfoStruct *AllocInfo;
|
||||
|
||||
typedef struct AllocInfoStruct {
|
||||
union {
|
||||
struct {
|
||||
Addr base;
|
||||
Size size;
|
||||
Pool pool;
|
||||
} tractData;
|
||||
struct {
|
||||
Seg seg;
|
||||
} segData;
|
||||
} the;
|
||||
} AllocInfoStruct;
|
||||
|
||||
typedef Res (*AllocFun)(AllocInfoStruct *aiReturn, LocusPref pref,
|
||||
Size size, Pool pool);
|
||||
|
||||
typedef void (*FreeFun)(AllocInfo ai);
|
||||
|
||||
typedef Bool (*FirstFun)(AllocInfoStruct *aiReturn, Arena arena);
|
||||
|
||||
typedef Bool (*NextFun)(AllocInfoStruct *nextReturn, AllocInfo ai,
|
||||
Arena arena);
|
||||
|
||||
typedef Count (*UnitsFun)(Count pages);
|
||||
|
||||
typedef void (*TestFun)(AllocInfo ai, Arena arena);
|
||||
|
||||
typedef void (*CopyFun)(AllocInfoStruct *toReturn, AllocInfo from);
|
||||
|
||||
|
||||
/* AllocatorClass -- encapsulates an allocation mechanism */
|
||||
|
||||
typedef struct AllocatorClassStruct *AllocatorClass;
|
||||
|
||||
typedef struct AllocatorClassStruct {
|
||||
AllocFun alloc; /* allocation method */
|
||||
FreeFun free; /* deallocation method */
|
||||
FirstFun first; /* find first block for iteration */
|
||||
NextFun next; /* find next block for iteration */
|
||||
UnitsFun units; /* number of iteration objects for pages */
|
||||
TestFun test; /* consistency check a region */
|
||||
CopyFun copy; /* copy an AllocationInfo object */
|
||||
} AllocatorClassStruct;
|
||||
|
||||
|
||||
/* tractSearchInChunk -- find a tract in a chunk
|
||||
*
|
||||
* .tract-search: Searches for a tract in the chunk starting at page
|
||||
* index i, return FALSE if there is none.
|
||||
*/
|
||||
|
||||
static Bool tractSearchInChunk(Tract *tractReturn, Chunk chunk, Index i)
|
||||
{
|
||||
AVER_CRITICAL(chunk->allocBase <= i);
|
||||
AVER_CRITICAL(i <= chunk->pages);
|
||||
|
||||
while (i < chunk->pages
|
||||
&& !(BTGet(chunk->allocTable, i)
|
||||
&& PageIsAllocated(ChunkPage(chunk, i)))) {
|
||||
++i;
|
||||
}
|
||||
if (i == chunk->pages)
|
||||
return FALSE;
|
||||
AVER(i < chunk->pages);
|
||||
*tractReturn = PageTract(ChunkPage(chunk, i));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* tractSearch -- find next tract above address
|
||||
*
|
||||
* Searches for the next tract in increasing address order.
|
||||
* The tract returned is the next one along from addr (i.e.,
|
||||
* it has a base address bigger than addr and no other tract
|
||||
* with a base address bigger than addr has a smaller base address).
|
||||
*
|
||||
* Returns FALSE if there is no tract to find (end of the arena).
|
||||
*/
|
||||
|
||||
static Bool tractSearch(Tract *tractReturn, Arena arena, Addr addr)
|
||||
{
|
||||
Bool b;
|
||||
Chunk chunk;
|
||||
Tree tree;
|
||||
|
||||
b = ChunkOfAddr(&chunk, arena, addr);
|
||||
if (b) {
|
||||
Index i;
|
||||
|
||||
i = INDEX_OF_ADDR(chunk, addr);
|
||||
/* There are fewer pages than addresses, therefore the */
|
||||
/* page index can never wrap around */
|
||||
AVER_CRITICAL(i+1 != 0);
|
||||
|
||||
if (tractSearchInChunk(tractReturn, chunk, i+1)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
while (TreeFindNext(&tree, ArenaChunkTree(arena), TreeKeyOfAddrVar(addr),
|
||||
ChunkCompare))
|
||||
{
|
||||
chunk = ChunkOfTree(tree);
|
||||
addr = chunk->base;
|
||||
/* Start from allocBase to skip the tables. */
|
||||
if (tractSearchInChunk(tractReturn, chunk, chunk->allocBase)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* Implementation of the tract-based interchangability interface */
|
||||
|
||||
static Res allocAsTract(AllocInfoStruct *aiReturn, LocusPref pref,
|
||||
Size size, Pool pool)
|
||||
{
|
||||
Res res;
|
||||
Addr base;
|
||||
res = ArenaAlloc(&base, pref, size, pool);
|
||||
if (res == ResOK) {
|
||||
aiReturn->the.tractData.base = base;
|
||||
aiReturn->the.tractData.size = size;
|
||||
aiReturn->the.tractData.pool = pool;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void freeAsTract(AllocInfo ai)
|
||||
{
|
||||
ArenaFree(ai->the.tractData.base,
|
||||
ai->the.tractData.size,
|
||||
ai->the.tractData.pool);
|
||||
}
|
||||
|
||||
static Bool firstAsTract(AllocInfoStruct *aiReturn, Arena arena)
|
||||
{
|
||||
Bool res;
|
||||
Tract tract;
|
||||
res = tractSearch(&tract, arena, 0);
|
||||
if (res) {
|
||||
aiReturn->the.tractData.base = TractBase(tract);
|
||||
aiReturn->the.tractData.size = ArenaGrainSize(arena);;
|
||||
aiReturn->the.tractData.pool = TractPool(tract);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Bool nextAsTract(AllocInfoStruct *nextReturn, AllocInfo ai,
|
||||
Arena arena)
|
||||
{
|
||||
Bool res;
|
||||
Tract tract;
|
||||
res = tractSearch(&tract, arena, ai->the.tractData.base);
|
||||
if (res) {
|
||||
nextReturn->the.tractData.base = TractBase(tract);
|
||||
nextReturn->the.tractData.size = ArenaGrainSize(arena);;
|
||||
nextReturn->the.tractData.pool = TractPool(tract);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Count unitsAsTract(Count pages)
|
||||
{
|
||||
return pages; /* one tract for each page */
|
||||
}
|
||||
|
||||
|
||||
static void testAsTract(AllocInfo ai, Arena arena)
|
||||
{
|
||||
/* Test TractOfAddr */
|
||||
Tract tract;
|
||||
Addr base;
|
||||
Bool found;
|
||||
|
||||
found = TractOfAddr(&tract, arena, ai->the.tractData.base);
|
||||
cdie(found, "TractOfAddr");
|
||||
base = TractBase(tract);
|
||||
cdie(base == ai->the.tractData.base, "base");
|
||||
|
||||
}
|
||||
|
||||
static void copyAsTract(AllocInfoStruct *toReturn, AllocInfo from)
|
||||
{
|
||||
toReturn->the.tractData.base = from->the.tractData.base;
|
||||
toReturn->the.tractData.size = from->the.tractData.size;
|
||||
toReturn->the.tractData.pool = from->the.tractData.pool;
|
||||
}
|
||||
|
||||
static AllocatorClassStruct allocatorTractStruct = {
|
||||
allocAsTract,
|
||||
freeAsTract,
|
||||
firstAsTract,
|
||||
nextAsTract,
|
||||
unitsAsTract,
|
||||
testAsTract,
|
||||
copyAsTract
|
||||
};
|
||||
|
||||
|
||||
/* Implementation of the segment-based interchangability interface */
|
||||
|
||||
static Res allocAsSeg(AllocInfoStruct *aiReturn, LocusPref pref,
|
||||
Size size, Pool pool)
|
||||
{
|
||||
Res res;
|
||||
Seg seg;
|
||||
res = SegAlloc(&seg, CLASS(Seg), pref, size, pool, argsNone);
|
||||
if (res == ResOK) {
|
||||
aiReturn->the.segData.seg = seg;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void freeAsSeg(AllocInfo ai)
|
||||
{
|
||||
SegFree(ai->the.segData.seg);
|
||||
}
|
||||
|
||||
static Bool firstAsSeg(AllocInfoStruct *aiReturn, Arena arena)
|
||||
{
|
||||
Bool res;
|
||||
Seg seg;
|
||||
res = SegFirst(&seg, arena);
|
||||
if (res) {
|
||||
aiReturn->the.segData.seg = seg;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Bool nextAsSeg(AllocInfoStruct *nextReturn, AllocInfo ai,
|
||||
Arena arena)
|
||||
{
|
||||
Bool res;
|
||||
Seg seg;
|
||||
res = SegNext(&seg, arena, ai->the.segData.seg);
|
||||
if (res) {
|
||||
nextReturn->the.segData.seg = seg;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Count unitsAsSeg(Count pages)
|
||||
{
|
||||
if (0 == pages)
|
||||
return 0; /* can't have a zero length seg */
|
||||
else
|
||||
return 1; /* one seg no matter how many pages */
|
||||
}
|
||||
|
||||
static void testAsSeg(AllocInfo ai, Arena arena)
|
||||
{
|
||||
/* Test size functions */
|
||||
Seg seg = ai->the.segData.seg;
|
||||
Addr base, limit;
|
||||
Size size;
|
||||
|
||||
UNUSED(arena);
|
||||
base = SegBase(seg);
|
||||
limit = SegLimit(seg);
|
||||
size = SegSize(seg);
|
||||
cdie(size == AddrOffset(base, limit), "size");
|
||||
}
|
||||
|
||||
static void copyAsSeg(AllocInfoStruct *toReturn, AllocInfo from)
|
||||
{
|
||||
toReturn->the.segData.seg = from->the.segData.seg;
|
||||
}
|
||||
|
||||
static AllocatorClassStruct allocatorSegStruct = {
|
||||
allocAsSeg,
|
||||
freeAsSeg,
|
||||
firstAsSeg,
|
||||
nextAsSeg,
|
||||
unitsAsSeg,
|
||||
testAsSeg,
|
||||
copyAsSeg
|
||||
};
|
||||
|
||||
|
||||
/* The main function can use either tracts or segs */
|
||||
|
||||
static void testAllocAndIterate(Arena arena, Pool pool,
|
||||
Size pageSize, Count numPerPage,
|
||||
AllocatorClass allocator)
|
||||
{
|
||||
AllocInfoStruct offsetRegion, gapRegion, newRegion, topRegion;
|
||||
LocusPrefStruct pref;
|
||||
Count offset, gap, new;
|
||||
ZoneSet zone = (ZoneSet)2;
|
||||
int i;
|
||||
|
||||
LocusPrefInit(&pref);
|
||||
|
||||
/* Testing the behaviour with various sizes of gaps in the page table. */
|
||||
|
||||
/* Assume the allocation strategy is first-fit. The idea of the tests is */
|
||||
/* to allocate a region of memory, then deallocate a gap in the middle, */
|
||||
/* then allocate a new region that fits in the gap with various amounts */
|
||||
/* left over. Like this: */
|
||||
/* |-offsetRegion-||----gapRegion----||-topRegion-| */
|
||||
/* |-offsetRegion-||-newRegion-| |-topRegion-| */
|
||||
/* This is done with three different sizes of offsetRegion, in two */
|
||||
/* different zones to ensure that all page boundary cases are tested. */
|
||||
for(i = 0; i < 2; ++i) { /* zone loop */
|
||||
for(offset = 0; offset <= 2*numPerPage; offset += numPerPage) {
|
||||
if(offset != 0)
|
||||
die(allocator->alloc(&offsetRegion, &pref, offset * pageSize, pool),
|
||||
"offsetRegion");
|
||||
for(gap = numPerPage+1; gap <= 3 * (numPerPage+1);
|
||||
gap += (numPerPage+1)) {
|
||||
die(allocator->alloc(&gapRegion, &pref, gap * pageSize, pool),
|
||||
"gapRegion");
|
||||
die(allocator->alloc(&topRegion, &pref, pageSize, pool),
|
||||
"topRegion");
|
||||
allocator->free(&gapRegion);
|
||||
for(new = 1; new <= gap; new += numPerPage) {
|
||||
AllocInfoStruct thisRegion, nextRegion;
|
||||
Count regionNum, expected;
|
||||
Res enoughRegions;
|
||||
|
||||
die(allocator->alloc(&newRegion, &pref, new * pageSize, pool),
|
||||
"newRegion");
|
||||
|
||||
/* Test iterators */
|
||||
cdie(allocator->first(&thisRegion, arena), "first");
|
||||
regionNum = 1;
|
||||
while (allocator->next(&nextRegion, &thisRegion, arena)) {
|
||||
regionNum++;
|
||||
allocator->copy(&thisRegion, &nextRegion);
|
||||
}
|
||||
|
||||
/* Should be able to iterate over at least offset, new, top */
|
||||
expected =
|
||||
allocator->units(offset) +
|
||||
allocator->units(new) +
|
||||
allocator->units(1);
|
||||
|
||||
if (regionNum >= expected)
|
||||
enoughRegions = ResOK;
|
||||
else
|
||||
enoughRegions = ResFAIL;
|
||||
|
||||
die(enoughRegions, "Not enough regions");
|
||||
|
||||
allocator->free(&newRegion);
|
||||
}
|
||||
|
||||
allocator->free(&topRegion);
|
||||
}
|
||||
if(offset != 0) {
|
||||
allocator->test(&offsetRegion, arena);
|
||||
allocator->free(&offsetRegion);
|
||||
}
|
||||
}
|
||||
LocusPrefExpress(&pref, LocusPrefZONESET, &zone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void testPageTable(ArenaClass klass, Size size, Addr addr, Bool zoned)
|
||||
{
|
||||
Arena arena; Pool pool;
|
||||
Size pageSize;
|
||||
Count tractsPerPage;
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, addr);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned);
|
||||
die(ArenaCreate(&arena, klass, args), "ArenaCreate");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
die(PoolCreate(&pool, arena, PoolClassMVFF(), argsNone), "PoolCreate");
|
||||
|
||||
pageSize = ArenaGrainSize(arena);
|
||||
tractsPerPage = pageSize / sizeof(TractStruct);
|
||||
printf("%ld tracts per page in the page table.\n", (long)tractsPerPage);
|
||||
|
||||
/* test tract allocation and iteration */
|
||||
testAllocAndIterate(arena, pool, pageSize, tractsPerPage,
|
||||
&allocatorTractStruct);
|
||||
|
||||
/* test segment allocation and iteration */
|
||||
testAllocAndIterate(arena, pool, pageSize, tractsPerPage,
|
||||
&allocatorSegStruct);
|
||||
|
||||
die(ArenaDescribe(arena, mps_lib_get_stdout(), 0), "ArenaDescribe");
|
||||
die(ArenaDescribeTracts(arena, mps_lib_get_stdout(), 0),
|
||||
"ArenaDescribeTracts");
|
||||
|
||||
PoolDestroy(pool);
|
||||
ArenaDestroy(arena);
|
||||
}
|
||||
|
||||
|
||||
/* testSize -- test arena size overflow
|
||||
*
|
||||
* Just try allocating larger arenas, doubling the size each time, until
|
||||
* it fails, then check the error code.
|
||||
*/
|
||||
|
||||
static void testSize(Size size)
|
||||
{
|
||||
ArenaClass klass = (ArenaClass)mps_arena_class_vm();
|
||||
Arena arena;
|
||||
Res res;
|
||||
|
||||
do {
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, size);
|
||||
res = ArenaCreate(&arena, klass, args);
|
||||
} MPS_ARGS_END(args);
|
||||
if (res == ResOK)
|
||||
ArenaDestroy(arena);
|
||||
else
|
||||
die((res == ResRESOURCE) ? ResOK : res, "right error code");
|
||||
size *= 2;
|
||||
} while (size == 0);
|
||||
}
|
||||
|
||||
|
||||
#define TEST_ARENA_SIZE ((Size)16<<22)
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *block;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0, TRUE);
|
||||
testPageTable((ArenaClass)mps_arena_class_vm(), TEST_ARENA_SIZE, 0, FALSE);
|
||||
|
||||
block = malloc(TEST_ARENA_SIZE);
|
||||
cdie(block != NULL, "malloc");
|
||||
testPageTable((ArenaClass)mps_arena_class_cl(), TEST_ARENA_SIZE, block, FALSE);
|
||||
|
||||
testSize(TEST_ARENA_SIZE);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
1360
mps/code/arenavm.c
Normal file
1360
mps/code/arenavm.c
Normal file
File diff suppressed because it is too large
Load diff
240
mps/code/arg.c
Normal file
240
mps/code/arg.c
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/* arg.c: ARGUMENT LISTS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2013-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .source: <design/keyword-arguments.rst>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "check.h"
|
||||
#include "mpm.h"
|
||||
#include "dbgpool.h"
|
||||
|
||||
SRCID(arg, "$Id$");
|
||||
|
||||
|
||||
/* ArgCheckCant -- default argument checker
|
||||
*
|
||||
* This is a default value for the KeyStruct check field for keywords
|
||||
* that don't have any meaningful checking they can do.
|
||||
*/
|
||||
|
||||
Bool ArgCheckCant(Arg arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static Bool ArgCheckShouldnt(Arg arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
NOTREACHED;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Bool ArgCheckFormat(Arg arg)
|
||||
{
|
||||
CHECKD(Format, arg->val.format);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckChain(Arg arg)
|
||||
{
|
||||
CHECKD(Chain, arg->val.chain);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckSize(Arg arg)
|
||||
{
|
||||
UNUSED(arg); /* TODO: Add and call SizeCheck */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckAddr(Arg arg)
|
||||
{
|
||||
UNUSED(arg); /* TODO: Add and call AddrCheck */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckPoolDebugOptions(Arg arg)
|
||||
{
|
||||
CHECKD_NOSIG(PoolDebugOptions, (PoolDebugOptions)arg->val.pool_debug_options);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckFun(Arg arg)
|
||||
{
|
||||
/* TODO: Fix potential pun here on Harvard architectures where
|
||||
function pointers are not compatible with other pointers. */
|
||||
CHECKL(FUNCHECK(arg->val.addr_method));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckAlign(Arg arg)
|
||||
{
|
||||
CHECKL(AlignCheck(arg->val.align));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckBool(Arg arg)
|
||||
{
|
||||
CHECKL(BoolCheck(arg->val.b));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckCount(Arg arg)
|
||||
{
|
||||
UNUSED(arg); /* TODO: Add and call CountCheck */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckPointer(Arg arg)
|
||||
{
|
||||
CHECKL(arg != NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckRankSet(Arg arg)
|
||||
{
|
||||
CHECKL(COMPATTYPE(RankSet, unsigned));
|
||||
CHECKL(RankSetCheck(arg->val.u));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckRank(Arg arg)
|
||||
{
|
||||
CHECKL(RankCheck(arg->val.rank));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckdouble(Arg arg)
|
||||
{
|
||||
/* Don't call isfinite() here because it's not in C89, and because
|
||||
infinity is a valid value for MPS_KEY_PAUSE_TIME. */
|
||||
UNUSED(arg);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Bool ArgCheckPool(Arg arg)
|
||||
{
|
||||
CHECKD(Pool, arg->val.pool);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
ARG_DEFINE_KEY(ARGS_END, Shouldnt);
|
||||
|
||||
ArgStruct mps_args_none[] = {{MPS_KEY_ARGS_END, {0}}};
|
||||
|
||||
|
||||
/* KeyCheck -- check the validity of an argument key */
|
||||
|
||||
Bool KeyCheck(Key key)
|
||||
{
|
||||
CHECKS(Key, key);
|
||||
CHECKL(key->name != NULL);
|
||||
CHECKL(FUNCHECK(key->check));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
Bool ArgCheck(Arg arg)
|
||||
{
|
||||
CHECKL(arg != NULL);
|
||||
CHECKD(Key, arg->key);
|
||||
CHECKL(arg->key->check(arg));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ArgCheck -- check the validity of an argument list */
|
||||
|
||||
Bool ArgListCheck(ArgList args)
|
||||
{
|
||||
Index i;
|
||||
CHECKL(args != NULL);
|
||||
for (i = 0; args[i].key != MPS_KEY_ARGS_END; ++i) {
|
||||
CHECKL(i < MPS_ARGS_MAX);
|
||||
CHECKD_NOSIG(Arg, &args[i]);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ArgPick -- try to pick an argument out of the argument list by keyword */
|
||||
|
||||
Bool ArgPick(ArgStruct *argOut, ArgList args, Key key)
|
||||
{
|
||||
Index i;
|
||||
|
||||
AVER(argOut != NULL);
|
||||
AVERT(ArgList, args);
|
||||
AVERT(Key, key);
|
||||
|
||||
for (i = 0; args[i].key != MPS_KEY_ARGS_END; ++i)
|
||||
if (args[i].key == key)
|
||||
goto found;
|
||||
return FALSE;
|
||||
|
||||
found:
|
||||
AVERT(Arg, &args[i]);
|
||||
*argOut = args[i];
|
||||
for(;;) {
|
||||
args[i] = args[i + 1];
|
||||
if (args[i].key == MPS_KEY_ARGS_END)
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* ArgRequire -- take a required argument out of the argument list by keyword */
|
||||
|
||||
void ArgRequire(ArgStruct *argOut, ArgList args, Key key)
|
||||
{
|
||||
Bool b = ArgPick(argOut, args, key);
|
||||
ASSERT(b, key->name);
|
||||
}
|
||||
|
||||
|
||||
/* ArgTrivVarargs -- class method to ignore deprecated varargs */
|
||||
|
||||
void ArgTrivVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs)
|
||||
{
|
||||
UNUSED(varargs);
|
||||
args[0].key = MPS_KEY_ARGS_END;
|
||||
AVERT(ArgList, args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
91
mps/code/arg.h
Normal file
91
mps/code/arg.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* arg.h: Keyword argument lists
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2013-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .source: <design/keyword-arguments.rst>.
|
||||
*/
|
||||
|
||||
#ifndef arg_h
|
||||
#define arg_h
|
||||
|
||||
#include "mpmtypes.h"
|
||||
|
||||
|
||||
/* KeyStruct -- keyword argument structure
|
||||
*
|
||||
* NOTE: Whether or not to have an argument checking field and functions
|
||||
* could be conditional on build variety. Checking arguments isn't on
|
||||
* the critical path, but this might save space if the MPS is being
|
||||
* deployed in a tight memory situation.
|
||||
*/
|
||||
|
||||
#define KeySig ((Sig)0x519CE111) /* SIGnature KEYyy */
|
||||
typedef struct mps_key_s {
|
||||
Sig sig; /* design.mps.sig.field */
|
||||
const char *name;
|
||||
Bool (*check)(Arg arg);
|
||||
} KeyStruct;
|
||||
|
||||
#define ARG_DEFINE_KEY(id, type) \
|
||||
extern const KeyStruct _mps_key_##id; \
|
||||
const KeyStruct _mps_key_##id = {KeySig, "MPS_KEY_" #id, ArgCheck##type}
|
||||
|
||||
#define argsNone mps_args_none
|
||||
|
||||
extern Bool KeyCheck(Key key);
|
||||
extern Bool ArgCheck(Arg arg);
|
||||
extern Bool ArgListCheck(ArgList args);
|
||||
|
||||
extern Bool ArgPick(ArgStruct *argOut, ArgList args, Key key);
|
||||
extern void ArgRequire(ArgStruct *argOut, ArgList args, Key key);
|
||||
extern void ArgTrivVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs);
|
||||
|
||||
extern Bool ArgCheckCant(Arg arg);
|
||||
extern Bool ArgCheckFormat(Arg arg);
|
||||
extern Bool ArgCheckChain(Arg arg);
|
||||
extern Bool ArgCheckSize(Arg arg);
|
||||
extern Bool ArgCheckAddr(Arg arg);
|
||||
extern Bool ArgCheckPoolDebugOptions(Arg arg);
|
||||
extern Bool ArgCheckFun(Arg arg);
|
||||
extern Bool ArgCheckAlign(Arg arg);
|
||||
extern Bool ArgCheckBool(Arg arg);
|
||||
extern Bool ArgCheckCount(Arg arg);
|
||||
extern Bool ArgCheckPointer(Arg arg);
|
||||
extern Bool ArgCheckRankSet(Arg arg);
|
||||
extern Bool ArgCheckRank(Arg arg);
|
||||
extern Bool ArgCheckdouble(Arg arg);
|
||||
extern Bool ArgCheckPool(Arg arg);
|
||||
|
||||
|
||||
#endif /* arg_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2013-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
396
mps/code/awlut.c
Normal file
396
mps/code/awlut.c
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
/* awlut.c: POOL CLASS AWL UNIT TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* .design: see <design/poolawl#.test>
|
||||
*/
|
||||
|
||||
#include "mpscawl.h"
|
||||
#include "mpsclo.h"
|
||||
#include "mpsavm.h"
|
||||
#include "fmtdy.h"
|
||||
#include "testlib.h"
|
||||
#include "testthr.h"
|
||||
#include "mpslib.h"
|
||||
#include "mps.h"
|
||||
#include "mpstd.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
#include <string.h> /* strlen */
|
||||
|
||||
|
||||
#define testArenaSIZE ((size_t)64<<20)
|
||||
#define TABLE_SLOTS 49
|
||||
#define ITERATIONS 5000
|
||||
#define CHATTER 100
|
||||
|
||||
|
||||
static mps_word_t bogus_class;
|
||||
|
||||
#define UNINIT 0x041412ED
|
||||
|
||||
#define DYLAN_ALIGN 4 /* depends on value defined in fmtdy.c */
|
||||
|
||||
|
||||
/* size_tAlignUp -- align w up to alignment a */
|
||||
|
||||
#define size_tAlignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1))
|
||||
|
||||
|
||||
static mps_word_t wrapper_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* Extra word */
|
||||
(mps_word_t)4<<2|2, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8), /* V */
|
||||
(mps_word_t)1<<2|1, /* VL */
|
||||
1 /* patterns */
|
||||
};
|
||||
|
||||
|
||||
static mps_word_t string_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* extra word */
|
||||
0, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8)|(mps_word_t)3<<3|4, /* V */
|
||||
1 /* VL */
|
||||
};
|
||||
|
||||
static mps_word_t table_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* extra word */
|
||||
(mps_word_t)1<<2|1, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8)|2, /* V */
|
||||
1 /* VL */
|
||||
};
|
||||
|
||||
|
||||
static void initialise_wrapper(mps_word_t *wrapper)
|
||||
{
|
||||
wrapper[0] = (mps_word_t)&wrapper_wrapper;
|
||||
wrapper[1] = (mps_word_t)&bogus_class;
|
||||
}
|
||||
|
||||
|
||||
/* alloc_string - create a dylan string object
|
||||
*
|
||||
* create a dylan string object (byte vector) whose contents
|
||||
* are the string s (including the terminating NUL)
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
|
||||
static mps_word_t *alloc_string(const char *s, mps_ap_t ap)
|
||||
{
|
||||
size_t l;
|
||||
size_t objsize;
|
||||
void *p;
|
||||
mps_word_t *object;
|
||||
|
||||
l = strlen(s)+1;
|
||||
/* number of words * sizeof word */
|
||||
objsize = (2 + (l+sizeof(mps_word_t)-1)/sizeof(mps_word_t))
|
||||
* sizeof(mps_word_t);
|
||||
objsize = size_tAlignUp(objsize, DYLAN_ALIGN);
|
||||
do {
|
||||
size_t i;
|
||||
char *s2;
|
||||
|
||||
die(mps_reserve(&p, ap, objsize), "Reserve Leaf\n");
|
||||
object = p;
|
||||
object[0] = (mps_word_t)string_wrapper;
|
||||
object[1] = l << 2 | 1;
|
||||
s2 = (char *)&object[2];
|
||||
for(i = 0; i < l; ++i) {
|
||||
s2[i] = s[i];
|
||||
}
|
||||
} while(!mps_commit(ap, p, objsize));
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/* alloc_table -- create a table with n variable slots
|
||||
*
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
|
||||
static mps_word_t *alloc_table(size_t n, mps_ap_t ap)
|
||||
{
|
||||
size_t objsize;
|
||||
void *p;
|
||||
mps_word_t *object;
|
||||
|
||||
objsize = (3 + n) * sizeof(mps_word_t);
|
||||
objsize = size_tAlignUp(objsize, MPS_PF_ALIGN);
|
||||
do {
|
||||
size_t i;
|
||||
|
||||
die(mps_reserve(&p, ap, objsize), "Reserve Table\n");
|
||||
object = p;
|
||||
object[0] = (mps_word_t)table_wrapper;
|
||||
object[1] = 0;
|
||||
object[2] = n << 2 | 1;
|
||||
for(i = 0; i < n; ++i) {
|
||||
object[3+i] = 0;
|
||||
}
|
||||
} while(!mps_commit(ap, p, objsize));
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/* gets the nth slot from a table
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
static mps_word_t *table_slot(mps_word_t *table, size_t n)
|
||||
{
|
||||
return (mps_word_t *)table[3+n];
|
||||
}
|
||||
|
||||
|
||||
/* sets the nth slot in a table
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
static void set_table_slot(mps_word_t *table, size_t n, mps_word_t *p)
|
||||
{
|
||||
cdie(table[0] == (mps_word_t)table_wrapper, "set_table_slot");
|
||||
table[3+n] = (mps_word_t)p;
|
||||
}
|
||||
|
||||
|
||||
/* links two tables together via their link slot
|
||||
* (1st fixed part slot)
|
||||
*/
|
||||
static void table_link(mps_word_t *t1, mps_word_t *t2)
|
||||
{
|
||||
cdie(t1[0] == (mps_word_t)table_wrapper, "table_link 1");
|
||||
cdie(t2[0] == (mps_word_t)table_wrapper, "table_link 2");
|
||||
t1[1] = (mps_word_t)t2;
|
||||
t2[1] = (mps_word_t)t1;
|
||||
}
|
||||
|
||||
|
||||
typedef struct tables_s {
|
||||
mps_arena_t arena;
|
||||
mps_word_t *weaktable;
|
||||
mps_word_t *exacttable;
|
||||
mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */
|
||||
/* table by referring to them */
|
||||
mps_ap_t weakap, exactap, bogusap, leafap;
|
||||
} tables_s, *tables_t;
|
||||
|
||||
|
||||
/* populate -- populate the weak table in a thread
|
||||
*
|
||||
* We use a thread to populate the table to avoid leaving any
|
||||
* references to objects in the table in registers, so that we can
|
||||
* test their weakness properly.
|
||||
*/
|
||||
|
||||
static void *populate(void *state)
|
||||
{
|
||||
tables_t tables = state;
|
||||
size_t i;
|
||||
mps_thr_t me;
|
||||
mps_root_t root;
|
||||
|
||||
die(mps_thread_reg(&me, tables->arena), "mps_thread_reg(populate)");
|
||||
die(mps_root_create_thread(&root, tables->arena, me, &state), "mps_root_create_thread(populate)");
|
||||
|
||||
tables->exacttable = alloc_table(TABLE_SLOTS, tables->exactap);
|
||||
tables->weaktable = alloc_table(TABLE_SLOTS, tables->weakap);
|
||||
table_link(tables->exacttable, tables->weaktable);
|
||||
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
mps_word_t *string;
|
||||
if (rnd() % 2 == 0) {
|
||||
string = alloc_string("iamalive", tables->leafap);
|
||||
tables->preserve[i] = string;
|
||||
} else {
|
||||
string = alloc_string("iamdead", tables->leafap);
|
||||
tables->preserve[i] = 0;
|
||||
}
|
||||
set_table_slot(tables->weaktable, i, string);
|
||||
string = alloc_string("iamexact", tables->leafap);
|
||||
set_table_slot(tables->exacttable, i, string);
|
||||
}
|
||||
|
||||
mps_root_destroy(root);
|
||||
mps_thread_dereg(me);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test(mps_arena_t arena,
|
||||
mps_ap_t leafap, mps_ap_t exactap, mps_ap_t weakap,
|
||||
mps_ap_t bogusap)
|
||||
{
|
||||
tables_s tables;
|
||||
size_t i, j;
|
||||
testthr_t thr;
|
||||
void *p;
|
||||
|
||||
/* Leave bogusap between reserve and commit for the duration */
|
||||
die(mps_reserve(&p, bogusap, 64), "Reserve bogus");
|
||||
|
||||
tables.arena = arena;
|
||||
tables.exactap = exactap;
|
||||
tables.weakap = weakap;
|
||||
tables.leafap = leafap;
|
||||
tables.bogusap = bogusap;
|
||||
|
||||
/* We using a thread for its pararallel execution, so just create
|
||||
and wait for it to finish. */
|
||||
testthr_create(&thr, populate, &tables);
|
||||
testthr_join(&thr, NULL);
|
||||
|
||||
for(j = 0; j < ITERATIONS; ++j) {
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
(void)alloc_string("spong", leafap);
|
||||
}
|
||||
}
|
||||
|
||||
die(mps_arena_collect(arena), "mps_arena_collect");
|
||||
mps_arena_release(arena);
|
||||
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
if (tables.preserve[i] == 0) {
|
||||
if (table_slot(tables.weaktable, i)) {
|
||||
error("Strongly unreachable weak table entry found, "
|
||||
"slot %"PRIuLONGEST".\n", (ulongest_t)i);
|
||||
} else {
|
||||
if (table_slot(tables.exacttable, i) != 0) {
|
||||
error("Weak table entry deleted, but corresponding "
|
||||
"exact table entry not deleted, slot %"PRIuLONGEST".\n",
|
||||
(ulongest_t)i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)mps_commit(bogusap, p, 64);
|
||||
}
|
||||
|
||||
|
||||
/* setup -- set up pools for the test
|
||||
*
|
||||
* guff serves two purposes:
|
||||
* - a pseudo stack base for the stack root.
|
||||
* - pointer to a guff structure, which packages some values needed
|
||||
* (arena and thr mostly)
|
||||
*/
|
||||
|
||||
struct guff_s {
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thr;
|
||||
};
|
||||
|
||||
ATTRIBUTE_NOINLINE
|
||||
static void setup(struct guff_s *guff)
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_pool_t leafpool;
|
||||
mps_pool_t tablepool;
|
||||
mps_fmt_t dylanfmt;
|
||||
mps_fmt_t dylanweakfmt;
|
||||
mps_ap_t leafap, exactap, weakap, bogusap;
|
||||
mps_root_t stack;
|
||||
mps_thr_t thr;
|
||||
|
||||
arena = guff->arena;
|
||||
thr = guff->thr;
|
||||
|
||||
die(mps_root_create_thread(&stack, arena, thr, guff),
|
||||
"Root Create\n");
|
||||
die(mps_fmt_create_A(&dylanfmt, arena, dylan_fmt_A()),
|
||||
"Format Create\n");
|
||||
die(mps_fmt_create_A(&dylanweakfmt, arena, dylan_fmt_A_weak()),
|
||||
"Format Create (weak)\n");
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt);
|
||||
die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args),
|
||||
"Leaf Pool Create\n");
|
||||
} MPS_ARGS_END(args);
|
||||
die(mps_pool_create(&tablepool, arena, mps_class_awl(), dylanweakfmt,
|
||||
dylan_weak_dependent),
|
||||
"Table Pool Create\n");
|
||||
die(mps_ap_create(&leafap, leafpool, mps_rank_exact()),
|
||||
"Leaf AP Create\n");
|
||||
die(mps_ap_create(&exactap, tablepool, mps_rank_exact()),
|
||||
"Exact AP Create\n");
|
||||
die(mps_ap_create(&weakap, tablepool, mps_rank_weak()),
|
||||
"Weak AP Create\n");
|
||||
die(mps_ap_create(&bogusap, tablepool, mps_rank_exact()),
|
||||
"Bogus AP Create\n");
|
||||
|
||||
test(arena, leafap, exactap, weakap, bogusap);
|
||||
|
||||
mps_ap_destroy(bogusap);
|
||||
mps_ap_destroy(weakap);
|
||||
mps_ap_destroy(exactap);
|
||||
mps_ap_destroy(leafap);
|
||||
mps_pool_destroy(tablepool);
|
||||
mps_pool_destroy(leafpool);
|
||||
mps_fmt_destroy(dylanweakfmt);
|
||||
mps_fmt_destroy(dylanfmt);
|
||||
mps_root_destroy(stack);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct guff_s guff;
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thread;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
initialise_wrapper(wrapper_wrapper);
|
||||
initialise_wrapper(string_wrapper);
|
||||
initialise_wrapper(table_wrapper);
|
||||
|
||||
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"arena_create\n");
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
guff.arena = arena;
|
||||
guff.thr = thread;
|
||||
setup(&guff);
|
||||
mps_thread_dereg(thread);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
398
mps/code/awluthe.c
Normal file
398
mps/code/awluthe.c
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
/* awluthe.c: POOL CLASS AWL UNIT TEST WITH OBJECT HEADERS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* .design: see <design/poolawl#.test>
|
||||
*/
|
||||
|
||||
#include "mpscawl.h"
|
||||
#include "mpsclo.h"
|
||||
#include "mpsavm.h"
|
||||
#include "fmthe.h"
|
||||
#include "fmtdy.h"
|
||||
#include "testlib.h"
|
||||
#include "testthr.h"
|
||||
#include "mpslib.h"
|
||||
#include "mps.h"
|
||||
#include "mpstd.h"
|
||||
|
||||
#include <string.h> /* strlen */
|
||||
#include <stdio.h> /* printf */
|
||||
|
||||
|
||||
#define testArenaSIZE ((size_t)64<<20)
|
||||
#define TABLE_SLOTS 49
|
||||
#define ITERATIONS 5000
|
||||
#define CHATTER 100
|
||||
|
||||
|
||||
static mps_word_t bogus_class;
|
||||
|
||||
#define UNINIT 0x041412ED
|
||||
|
||||
#define DYLAN_ALIGN 4 /* depends on value defined in fmtdy.c */
|
||||
|
||||
|
||||
/* size_tAlignUp -- align w up to alignment a */
|
||||
|
||||
#define size_tAlignUp(w, a) (((w) + (a) - 1) & ~((size_t)(a) - 1))
|
||||
|
||||
|
||||
static mps_word_t wrapper_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* Extra word */
|
||||
(mps_word_t)4<<2|2, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8), /* V */
|
||||
(mps_word_t)1<<2|1, /* VL */
|
||||
1 /* patterns */
|
||||
};
|
||||
|
||||
|
||||
static mps_word_t string_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* extra word */
|
||||
0, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8)|(mps_word_t)3<<3|4, /* V */
|
||||
1 /* VL */
|
||||
};
|
||||
|
||||
static mps_word_t table_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* extra word */
|
||||
(mps_word_t)1<<2|1, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8)|2, /* V */
|
||||
1 /* VL */
|
||||
};
|
||||
|
||||
|
||||
static void initialise_wrapper(mps_word_t *wrapper)
|
||||
{
|
||||
wrapper[0] = (mps_word_t)&wrapper_wrapper;
|
||||
wrapper[1] = (mps_word_t)&bogus_class;
|
||||
}
|
||||
|
||||
|
||||
/* alloc_string - create a dylan string object
|
||||
*
|
||||
* create a dylan string object (byte vector) whose contents
|
||||
* are the string s (including the terminating NUL)
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
|
||||
static mps_word_t *alloc_string(const char *s, mps_ap_t ap)
|
||||
{
|
||||
size_t l;
|
||||
size_t objsize;
|
||||
void *p;
|
||||
mps_word_t *object;
|
||||
|
||||
l = strlen(s)+1;
|
||||
/* number of words * sizeof word */
|
||||
objsize = (2 + (l+sizeof(mps_word_t)-1)/sizeof(mps_word_t))
|
||||
* sizeof(mps_word_t);
|
||||
objsize = size_tAlignUp(objsize, DYLAN_ALIGN);
|
||||
do {
|
||||
size_t i;
|
||||
char *s2;
|
||||
|
||||
die(mps_reserve(&p, ap, objsize + headerSIZE), "Reserve Leaf\n");
|
||||
object = (mps_word_t *)((char *)p + headerSIZE);
|
||||
object[0] = (mps_word_t)string_wrapper;
|
||||
object[1] = l << 2 | 1;
|
||||
s2 = (char *)&object[2];
|
||||
for(i = 0; i < l; ++i) {
|
||||
s2[i] = s[i];
|
||||
}
|
||||
((int*)p)[0] = realHeader;
|
||||
((int*)p)[1] = 0xED0ED;
|
||||
} while(!mps_commit(ap, p, objsize + headerSIZE));
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/* alloc_table -- create a table with n variable slots
|
||||
*
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
|
||||
static mps_word_t *alloc_table(size_t n, mps_ap_t ap)
|
||||
{
|
||||
size_t objsize;
|
||||
void *p;
|
||||
mps_word_t *object;
|
||||
|
||||
objsize = (3 + n) * sizeof(mps_word_t);
|
||||
objsize = size_tAlignUp(objsize, MPS_PF_ALIGN);
|
||||
do {
|
||||
size_t i;
|
||||
|
||||
die(mps_reserve(&p, ap, objsize + headerSIZE), "Reserve Table\n");
|
||||
object = (mps_word_t *)((char *)p + headerSIZE);
|
||||
object[0] = (mps_word_t)table_wrapper;
|
||||
object[1] = 0;
|
||||
object[2] = n << 2 | 1;
|
||||
for(i = 0; i < n; ++i) {
|
||||
object[3+i] = 0;
|
||||
}
|
||||
((int*)p)[0] = realHeader;
|
||||
((int*)p)[1] = 0xED0ED;
|
||||
} while(!mps_commit(ap, p, objsize + headerSIZE));
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/* gets the nth slot from a table
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
static mps_word_t *table_slot(mps_word_t *table, size_t n)
|
||||
{
|
||||
return (mps_word_t *)table[3+n];
|
||||
}
|
||||
|
||||
|
||||
/* sets the nth slot in a table
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
static void set_table_slot(mps_word_t *table, size_t n, mps_word_t *p)
|
||||
{
|
||||
cdie(table[0] == (mps_word_t)table_wrapper, "set_table_slot");
|
||||
table[3+n] = (mps_word_t)p;
|
||||
}
|
||||
|
||||
|
||||
/* links two tables together via their link slot
|
||||
* (1st fixed part slot)
|
||||
*/
|
||||
static void table_link(mps_word_t *t1, mps_word_t *t2)
|
||||
{
|
||||
cdie(t1[0] == (mps_word_t)table_wrapper, "table_link 1");
|
||||
cdie(t2[0] == (mps_word_t)table_wrapper, "table_link 2");
|
||||
t1[1] = (mps_word_t)t2;
|
||||
t2[1] = (mps_word_t)t1;
|
||||
}
|
||||
|
||||
|
||||
typedef struct tables_s {
|
||||
mps_arena_t arena;
|
||||
mps_word_t *weaktable;
|
||||
mps_word_t *exacttable;
|
||||
mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */
|
||||
/* table by referring to them */
|
||||
mps_ap_t weakap, exactap, bogusap, leafap;
|
||||
} tables_s, *tables_t;
|
||||
|
||||
/* populate -- populate the weak table in a thread
|
||||
*
|
||||
* We use a thread to populate the table to avoid leaving any
|
||||
* references to objects in the table in registers, so that we can
|
||||
* test their weakness properly.
|
||||
*/
|
||||
|
||||
static void *populate(void *state)
|
||||
{
|
||||
tables_t tables = state;
|
||||
size_t i;
|
||||
mps_thr_t me;
|
||||
mps_root_t root;
|
||||
|
||||
die(mps_thread_reg(&me, tables->arena), "mps_thread_reg(populate)");
|
||||
die(mps_root_create_thread(&root, tables->arena, me, &state), "mps_root_create_thread(populate)");
|
||||
|
||||
tables->exacttable = alloc_table(TABLE_SLOTS, tables->exactap);
|
||||
tables->weaktable = alloc_table(TABLE_SLOTS, tables->weakap);
|
||||
table_link(tables->exacttable, tables->weaktable);
|
||||
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
mps_word_t *string;
|
||||
if (rnd() % 2 == 0) {
|
||||
string = alloc_string("iamalive", tables->leafap);
|
||||
tables->preserve[i] = string;
|
||||
} else {
|
||||
string = alloc_string("iamdead", tables->leafap);
|
||||
tables->preserve[i] = 0;
|
||||
}
|
||||
set_table_slot(tables->weaktable, i, string);
|
||||
string = alloc_string("iamexact", tables->leafap);
|
||||
set_table_slot(tables->exacttable, i, string);
|
||||
}
|
||||
|
||||
mps_root_destroy(root);
|
||||
mps_thread_dereg(me);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test(mps_arena_t arena,
|
||||
mps_ap_t leafap, mps_ap_t exactap, mps_ap_t weakap,
|
||||
mps_ap_t bogusap)
|
||||
{
|
||||
tables_s tables;
|
||||
size_t i, j;
|
||||
testthr_t thr;
|
||||
void *p;
|
||||
|
||||
/* Leave bogusap between reserve and commit for the duration */
|
||||
die(mps_reserve(&p, bogusap, 64), "Reserve bogus");
|
||||
|
||||
tables.arena = arena;
|
||||
tables.exactap = exactap;
|
||||
tables.weakap = weakap;
|
||||
tables.leafap = leafap;
|
||||
tables.bogusap = bogusap;
|
||||
|
||||
/* We using a thread for its pararallel execution, so just create
|
||||
and wait for it to finish. */
|
||||
testthr_create(&thr, populate, &tables);
|
||||
testthr_join(&thr, NULL);
|
||||
|
||||
for(j = 0; j < ITERATIONS; ++j) {
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
(void)alloc_string("spong", leafap);
|
||||
}
|
||||
}
|
||||
|
||||
die(mps_arena_collect(arena), "mps_arena_collect");
|
||||
mps_arena_release(arena);
|
||||
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
if (tables.preserve[i] == 0) {
|
||||
if (table_slot(tables.weaktable, i)) {
|
||||
error("Strongly unreachable weak table entry found, "
|
||||
"slot %"PRIuLONGEST".\n", (ulongest_t)i);
|
||||
} else {
|
||||
if (table_slot(tables.exacttable, i) != 0) {
|
||||
error("Weak table entry deleted, but corresponding "
|
||||
"exact table entry not deleted, slot %"PRIuLONGEST".\n",
|
||||
(ulongest_t)i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)mps_commit(bogusap, p, 64);
|
||||
}
|
||||
|
||||
|
||||
/* setup -- set up pools for the test
|
||||
*
|
||||
* guff serves two purposes:
|
||||
* - a pseudo stack base for the stack root.
|
||||
* - pointer to a guff structure, which packages some values needed
|
||||
* (arena and thr mostly)
|
||||
*/
|
||||
|
||||
struct guff_s {
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thr;
|
||||
};
|
||||
|
||||
ATTRIBUTE_NOINLINE
|
||||
static void setup(struct guff_s *guff)
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_pool_t leafpool;
|
||||
mps_pool_t tablepool;
|
||||
mps_fmt_t dylanfmt;
|
||||
mps_fmt_t dylanweakfmt;
|
||||
mps_ap_t leafap, exactap, weakap, bogusap;
|
||||
mps_root_t stack;
|
||||
mps_thr_t thr;
|
||||
|
||||
arena = guff->arena;
|
||||
thr = guff->thr;
|
||||
|
||||
die(mps_root_create_thread(&stack, arena, thr, guff),
|
||||
"Root Create\n");
|
||||
die(EnsureHeaderFormat(&dylanfmt, arena), "EnsureHeaderFormat");
|
||||
die(EnsureHeaderWeakFormat(&dylanweakfmt, arena), "EnsureHeaderWeakFormat");
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt);
|
||||
die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args),
|
||||
"Leaf Pool Create\n");
|
||||
} MPS_ARGS_END(args);
|
||||
die(mps_pool_create(&tablepool, arena, mps_class_awl(), dylanweakfmt,
|
||||
dylan_weak_dependent),
|
||||
"Table Pool Create\n");
|
||||
die(mps_ap_create(&leafap, leafpool, mps_rank_exact()),
|
||||
"Leaf AP Create\n");
|
||||
die(mps_ap_create(&exactap, tablepool, mps_rank_exact()),
|
||||
"Exact AP Create\n");
|
||||
die(mps_ap_create(&weakap, tablepool, mps_rank_weak()),
|
||||
"Weak AP Create\n");
|
||||
die(mps_ap_create(&bogusap, tablepool, mps_rank_exact()),
|
||||
"Bogus AP Create\n");
|
||||
|
||||
test(arena, leafap, exactap, weakap, bogusap);
|
||||
|
||||
mps_ap_destroy(bogusap);
|
||||
mps_ap_destroy(weakap);
|
||||
mps_ap_destroy(exactap);
|
||||
mps_ap_destroy(leafap);
|
||||
mps_pool_destroy(tablepool);
|
||||
mps_pool_destroy(leafpool);
|
||||
mps_fmt_destroy(dylanweakfmt);
|
||||
mps_fmt_destroy(dylanfmt);
|
||||
mps_root_destroy(stack);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct guff_s guff;
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thread;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
initialise_wrapper(wrapper_wrapper);
|
||||
initialise_wrapper(string_wrapper);
|
||||
initialise_wrapper(table_wrapper);
|
||||
|
||||
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"arena_create\n");
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
guff.arena = arena;
|
||||
guff.thr = thread;
|
||||
setup(&guff);
|
||||
mps_thread_dereg(thread);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
355
mps/code/awlutth.c
Normal file
355
mps/code/awlutth.c
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/* awlutth.c: THREADING UNIT TEST USING POOL CLASS AWL
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* .design: see <design/poolawl#.test>
|
||||
*/
|
||||
|
||||
#include "mpscawl.h"
|
||||
#include "mpsclo.h"
|
||||
#include "mpsavm.h"
|
||||
#include "fmtdy.h"
|
||||
#include "testlib.h"
|
||||
#include "testthr.h"
|
||||
#include "mpslib.h"
|
||||
#include "mps.h"
|
||||
#include "mpstd.h"
|
||||
|
||||
#include <stdio.h> /* printf, puts */
|
||||
#include <string.h> /* strlen */
|
||||
|
||||
|
||||
#define testArenaSIZE ((size_t)64<<20)
|
||||
#define TABLE_SLOTS 50
|
||||
#define ITERATIONS 5000
|
||||
#define CHATTER 100
|
||||
/* The number that a half of all numbers generated from rnd are less
|
||||
* than. Hence, probability a-half, or P a-half */
|
||||
/* see <code/testlib.h> */
|
||||
#define P_A_HALF (1024uL*1024uL*1024uL - 1) /* 2^30 - 1 */
|
||||
|
||||
|
||||
static mps_word_t bogus_class;
|
||||
|
||||
#define UNINIT 0x041412ED
|
||||
|
||||
#define DYLAN_ALIGN 4 /* depends on value defined in fmtdy.c */
|
||||
|
||||
|
||||
static mps_word_t wrapper_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* Extra word */
|
||||
(mps_word_t)4<<2|2, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8), /* V (version 2) */
|
||||
(mps_word_t)1<<2|1, /* VL */
|
||||
1 /* patterns */
|
||||
};
|
||||
|
||||
|
||||
static mps_word_t string_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* extra word */
|
||||
0, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8)|(mps_word_t)3<<3|4, /* V */
|
||||
1 /* VL */
|
||||
};
|
||||
|
||||
static mps_word_t table_wrapper[] = {
|
||||
UNINIT, /* wrapper */
|
||||
UNINIT, /* class */
|
||||
0, /* extra word */
|
||||
(mps_word_t)1<<2|1, /* F */
|
||||
(mps_word_t)2<<(MPS_WORD_WIDTH - 8)|2, /* V */
|
||||
1 /* VL */
|
||||
};
|
||||
|
||||
|
||||
static void initialise_wrapper(mps_word_t *wrapper)
|
||||
{
|
||||
wrapper[0] = (mps_word_t)&wrapper_wrapper;
|
||||
wrapper[1] = (mps_word_t)&bogus_class;
|
||||
}
|
||||
|
||||
|
||||
/* create a dylan string object (byte vector) whose contents
|
||||
* are the string s (including the terminating NUL)
|
||||
* .assume.dylan-obj */
|
||||
static mps_word_t *alloc_string(const char *s, mps_ap_t ap)
|
||||
{
|
||||
size_t l;
|
||||
size_t objsize;
|
||||
void *p;
|
||||
mps_word_t *object;
|
||||
|
||||
l = strlen(s)+1;
|
||||
/* number of words * sizeof word */
|
||||
objsize = (2 + (l+sizeof(mps_word_t)-1)/sizeof(mps_word_t)) *
|
||||
sizeof(mps_word_t);
|
||||
objsize = (objsize + DYLAN_ALIGN-1)/DYLAN_ALIGN*DYLAN_ALIGN;
|
||||
do {
|
||||
size_t i;
|
||||
char *s2;
|
||||
|
||||
die(mps_reserve(&p, ap, objsize), "Reserve Leaf\n");
|
||||
object = p;
|
||||
object[0] = (mps_word_t)string_wrapper;
|
||||
object[1] = l << 2 | 1;
|
||||
s2 = (char *)&object[2];
|
||||
for(i = 0; i < l; ++i) {
|
||||
s2[i] = s[i];
|
||||
}
|
||||
} while(!mps_commit(ap, p, objsize));
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/* alloc_table -- create a table with n variable slots
|
||||
*
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
static mps_word_t *alloc_table(unsigned long n, mps_ap_t ap)
|
||||
{
|
||||
size_t objsize;
|
||||
void *p;
|
||||
mps_word_t *object;
|
||||
objsize = (3 + n) * sizeof(mps_word_t);
|
||||
objsize = (objsize + MPS_PF_ALIGN-1)/MPS_PF_ALIGN*MPS_PF_ALIGN;
|
||||
do {
|
||||
unsigned long i;
|
||||
|
||||
die(mps_reserve(&p, ap, objsize), "Reserve Table\n");
|
||||
object = p;
|
||||
object[0] = (mps_word_t)table_wrapper;
|
||||
object[1] = 0;
|
||||
object[2] = n << 2 | 1;
|
||||
for(i = 0; i < n; ++i) {
|
||||
object[3+i] = 0;
|
||||
}
|
||||
} while(!mps_commit(ap, p, objsize));
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/* gets the nth slot from a table
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
static mps_word_t *table_slot(mps_word_t *table, unsigned long n)
|
||||
{
|
||||
return (mps_word_t *)table[3+n];
|
||||
}
|
||||
|
||||
/* sets the nth slot in a table
|
||||
* .assume.dylan-obj
|
||||
*/
|
||||
static void set_table_slot(mps_word_t *table,
|
||||
unsigned long n, mps_word_t *p)
|
||||
{
|
||||
cdie(table[0] == (mps_word_t)table_wrapper, "set_table_slot");
|
||||
table[3+n] = (mps_word_t)p;
|
||||
}
|
||||
|
||||
/* links two tables together via their link slot
|
||||
* (1st fixed part slot)
|
||||
*/
|
||||
static void table_link(mps_word_t *t1, mps_word_t *t2)
|
||||
{
|
||||
cdie(t1[0] == (mps_word_t)table_wrapper, "table_link 1");
|
||||
cdie(t2[0] == (mps_word_t)table_wrapper, "table_link 2");
|
||||
t1[1] = (mps_word_t)t2;
|
||||
t2[1] = (mps_word_t)t1;
|
||||
}
|
||||
|
||||
|
||||
static void test(mps_ap_t leafap, mps_ap_t exactap, mps_ap_t weakap,
|
||||
mps_ap_t bogusap)
|
||||
{
|
||||
mps_word_t *weaktable;
|
||||
mps_word_t *exacttable;
|
||||
mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */
|
||||
/* table by referring to them */
|
||||
unsigned long i, j;
|
||||
void *p;
|
||||
|
||||
exacttable = alloc_table(TABLE_SLOTS, exactap);
|
||||
weaktable = alloc_table(TABLE_SLOTS, weakap);
|
||||
table_link(exacttable, weaktable);
|
||||
|
||||
/* Leave bogusap between reserve and commit for the duration */
|
||||
die(mps_reserve(&p, bogusap, 64), "Reserve bogus");
|
||||
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
mps_word_t *string;
|
||||
if(rnd() < P_A_HALF) {
|
||||
string = alloc_string("iamalive", leafap);
|
||||
preserve[i] = string;
|
||||
} else {
|
||||
string = alloc_string("iamdead", leafap);
|
||||
preserve[i] = 0;
|
||||
}
|
||||
set_table_slot(weaktable, i, string);
|
||||
string = alloc_string("iamexact", leafap);
|
||||
set_table_slot(exacttable, i, string);
|
||||
}
|
||||
|
||||
for(j = 0; j < ITERATIONS; ++j) {
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
mps_word_t *string;
|
||||
|
||||
string = alloc_string("spong", leafap);
|
||||
UNUSED(string);
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < TABLE_SLOTS; ++i) {
|
||||
if(preserve[i] == 0) {
|
||||
if(table_slot(weaktable, i)) {
|
||||
fprintf(stdout,
|
||||
"Strongly unreachable weak table entry found, "
|
||||
"slot %lu.\n",
|
||||
i);
|
||||
} else {
|
||||
if(table_slot(exacttable, i) != 0) {
|
||||
fprintf(stdout,
|
||||
"Weak table entry deleted, but corresponding "
|
||||
"exact table entry not deleted, slot %lu.\n",
|
||||
i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void)mps_commit(bogusap, p, 64);
|
||||
puts("A okay\n");
|
||||
}
|
||||
|
||||
|
||||
struct guff_s {
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thr;
|
||||
};
|
||||
|
||||
/* guff serves two purposes:
|
||||
* A pseudo stack base for the stack root.
|
||||
* Pointer to a guff structure, which packages some values needed
|
||||
* (arena and thr mostly) */
|
||||
ATTRIBUTE_NOINLINE
|
||||
static void setup(struct guff_s *guff)
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_pool_t leafpool;
|
||||
mps_pool_t tablepool;
|
||||
mps_fmt_t dylanfmt;
|
||||
mps_fmt_t dylanweakfmt;
|
||||
mps_ap_t leafap, exactap, weakap, bogusap;
|
||||
mps_root_t stack;
|
||||
mps_thr_t thr;
|
||||
|
||||
arena = guff->arena;
|
||||
thr = guff->thr;
|
||||
|
||||
die(mps_root_create_thread(&stack, arena, thr, guff),
|
||||
"Root Create\n");
|
||||
die(mps_fmt_create_A(&dylanfmt, arena, dylan_fmt_A()),
|
||||
"Format Create\n");
|
||||
die(mps_fmt_create_A(&dylanweakfmt, arena, dylan_fmt_A_weak()),
|
||||
"Format Create (weak)\n");
|
||||
die(mps_pool_create(&leafpool, arena, mps_class_lo(), dylanfmt),
|
||||
"Leaf Pool Create\n");
|
||||
die(mps_pool_create(&tablepool, arena, mps_class_awl(), dylanweakfmt,
|
||||
dylan_weak_dependent),
|
||||
"Table Pool Create\n");
|
||||
die(mps_ap_create(&leafap, leafpool, mps_rank_exact()),
|
||||
"Leaf AP Create\n");
|
||||
die(mps_ap_create(&exactap, tablepool, mps_rank_exact()),
|
||||
"Exact AP Create\n");
|
||||
die(mps_ap_create(&weakap, tablepool, mps_rank_weak()),
|
||||
"Weak AP Create\n");
|
||||
die(mps_ap_create(&bogusap, tablepool, mps_rank_exact()),
|
||||
"Bogus AP Create\n");
|
||||
|
||||
test(leafap, exactap, weakap, bogusap);
|
||||
|
||||
mps_ap_destroy(bogusap);
|
||||
mps_ap_destroy(weakap);
|
||||
mps_ap_destroy(exactap);
|
||||
mps_ap_destroy(leafap);
|
||||
mps_pool_destroy(tablepool);
|
||||
mps_pool_destroy(leafpool);
|
||||
mps_fmt_destroy(dylanweakfmt);
|
||||
mps_fmt_destroy(dylanfmt);
|
||||
mps_root_destroy(stack);
|
||||
}
|
||||
|
||||
|
||||
static void *setup_thr(void *v)
|
||||
{
|
||||
struct guff_s guff;
|
||||
mps_arena_t arena = v;
|
||||
mps_thr_t thread;
|
||||
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg");
|
||||
guff.arena = arena;
|
||||
guff.thr = thread;
|
||||
setup(&guff);
|
||||
mps_thread_dereg(thread);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
testthr_t thread1;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
initialise_wrapper(wrapper_wrapper);
|
||||
initialise_wrapper(string_wrapper);
|
||||
initialise_wrapper(table_wrapper);
|
||||
|
||||
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"arena_create\n");
|
||||
testthr_create(&thread1, setup_thr, arena);
|
||||
setup_thr(arena);
|
||||
testthr_join(&thread1, NULL);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
155
mps/code/boot.c
Normal file
155
mps/code/boot.c
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/* boot.c: BOOTSTRAP ALLOCATOR
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview: A structure and protocols for allocating memory from a
|
||||
* given block. Very simple, it basically just increments a pointer.
|
||||
*
|
||||
* .boot.c: The Bootstrap Allocator is used to allocate C structures
|
||||
* for use in the implementation, not client objects. Therefore,
|
||||
* we use "C types" (void *, size_t) not "client types" (Addr, Size).
|
||||
*/
|
||||
|
||||
#include "boot.h"
|
||||
#include "mpm.h"
|
||||
|
||||
SRCID(boot, "$Id$");
|
||||
|
||||
|
||||
#define BootBlockSig ((Sig)0x519B002B) /* SIGnature BOOT Block */
|
||||
|
||||
|
||||
/* BootBlockCheck -- check a BootBlock structure */
|
||||
|
||||
Bool BootBlockCheck(BootBlock boot)
|
||||
{
|
||||
CHECKS(BootBlock, boot);
|
||||
CHECKL(boot->base != NULL);
|
||||
CHECKL(boot->alloc != NULL);
|
||||
CHECKL(boot->limit != NULL);
|
||||
CHECKL(boot->base <= boot->alloc);
|
||||
CHECKL(boot->alloc <= boot->limit);
|
||||
CHECKL(boot->base < boot->limit);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* BootBlockInit -- initialize a BootBlock
|
||||
*
|
||||
* boot: a pointer to the structure to be initialized
|
||||
* (must have been allocated by the caller, probably on the stack).
|
||||
* base: a pointer to the base of the memory to be allocated from
|
||||
* from (the memory need not be committed)
|
||||
* limit: a pointer to the limit of the memory to be allocated from
|
||||
*/
|
||||
|
||||
Res BootBlockInit(BootBlockStruct *boot, void *base, void *limit)
|
||||
{
|
||||
/* Can't check boot as we are supposed to be initializing it */
|
||||
AVER(boot != NULL);
|
||||
AVER(base != NULL);
|
||||
AVER(limit != NULL);
|
||||
AVER(base < limit);
|
||||
|
||||
boot->base = base;
|
||||
boot->alloc = base;
|
||||
boot->limit = limit;
|
||||
boot->sig = BootBlockSig;
|
||||
|
||||
AVERT(BootBlock, boot);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* BootBlockFinish -- finish a BootBlock structure */
|
||||
|
||||
void BootBlockFinish(BootBlock boot)
|
||||
{
|
||||
AVERT(BootBlock, boot);
|
||||
|
||||
boot->base = boot->alloc = boot->limit = NULL;
|
||||
boot->sig = SigInvalid;
|
||||
}
|
||||
|
||||
|
||||
/* BootAllocated
|
||||
*
|
||||
* Returns the total amount allocated using this descriptor
|
||||
*/
|
||||
size_t BootAllocated(BootBlock boot)
|
||||
{
|
||||
AVERT(BootBlock, boot);
|
||||
|
||||
return PointerOffset(boot->base, boot->alloc);
|
||||
}
|
||||
|
||||
|
||||
/* BootAlloc -- allocate from BootBlock structure
|
||||
*
|
||||
* preturn: The returned pointer, see .boot.c.
|
||||
* boot: must have been initialized with BootBlockInit().
|
||||
* size: size of requested object, see .boot.c.
|
||||
* align: required alignment of object, see .boot.c.
|
||||
*/
|
||||
|
||||
Res BootAlloc(void **pReturn, BootBlock boot, size_t size, size_t align)
|
||||
{
|
||||
void *blockBase, *blockLimit; /* base, limit of candidate block */
|
||||
|
||||
AVER(pReturn != NULL);
|
||||
AVERT(BootBlock, boot);
|
||||
AVER(size > 0);
|
||||
AVERT(Align, (Align)align);
|
||||
|
||||
/* Align alloc pointer up and bounds check. */
|
||||
blockBase = PointerAlignUp(boot->alloc, align);
|
||||
if(boot->limit <= blockBase || blockBase < boot->alloc) {
|
||||
return ResMEMORY;
|
||||
}
|
||||
blockLimit = PointerAdd(blockBase, size);
|
||||
/* Following checks that the ordering constraint holds: */
|
||||
/* boot->alloc <= blockBase < blockLimit <= boot->limit */
|
||||
/* (if it doesn't hold then something overallocated/wrapped round) */
|
||||
if(blockBase < boot->alloc ||
|
||||
blockLimit <= blockBase ||
|
||||
boot->limit < blockLimit) {
|
||||
return ResMEMORY;
|
||||
}
|
||||
|
||||
/* Fits! So allocate it */
|
||||
boot->alloc = blockLimit;
|
||||
*pReturn = blockBase;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
64
mps/code/boot.h
Normal file
64
mps/code/boot.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* boot.h: BOOTSTRAP ALLOCATOR INTERFACE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview: A protocol for allocating memory from a given block.
|
||||
*/
|
||||
|
||||
#ifndef boot_h
|
||||
#define boot_h
|
||||
|
||||
#include "mpmtypes.h"
|
||||
|
||||
|
||||
/* BootBlockStruct -- descriptor of the block to allocate from */
|
||||
|
||||
typedef struct BootBlockStruct
|
||||
{
|
||||
Sig sig; /* design.mps.sig.field */
|
||||
void *base;
|
||||
void *alloc;
|
||||
void *limit;
|
||||
} BootBlockStruct;
|
||||
|
||||
|
||||
extern Res BootBlockInit(BootBlockStruct *boot, void *base, void *limit);
|
||||
extern void BootBlockFinish(BootBlock boot);
|
||||
extern Res BootAlloc(void **pReturn, BootBlock boot, size_t size,
|
||||
size_t align);
|
||||
extern size_t BootAllocated(BootBlock boot);
|
||||
extern Bool BootBlockCheck(BootBlock boot);
|
||||
|
||||
|
||||
#endif /* boot_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
1064
mps/code/bt.c
Normal file
1064
mps/code/bt.c
Normal file
File diff suppressed because it is too large
Load diff
105
mps/code/bt.h
Normal file
105
mps/code/bt.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* bt.h: Bit Table Interface
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .source: <design/bt>
|
||||
*/
|
||||
|
||||
#ifndef bt_h
|
||||
#define bt_h
|
||||
|
||||
#include "mpmtypes.h"
|
||||
|
||||
|
||||
/* <design/bt#if.size> */
|
||||
extern Size (BTSize)(Count length);
|
||||
#define BTSize(n) (((n) + MPS_WORD_WIDTH-1) / MPS_WORD_WIDTH * sizeof(Word))
|
||||
|
||||
/* <design/bt#.if.get> */
|
||||
extern Bool (BTGet)(BT bt, Index index);
|
||||
#define BTGet(a, i) \
|
||||
((Bool)(((a)[((i) >> MPS_WORD_SHIFT)] \
|
||||
>> ((i) & ~((Word)-1 << MPS_WORD_SHIFT))) \
|
||||
& (Word)1))
|
||||
|
||||
/* <design/bt#.if.set> */
|
||||
extern void (BTSet)(BT bt, Index index);
|
||||
#define BTSet(a, i) \
|
||||
BEGIN \
|
||||
(a)[((i)>>MPS_WORD_SHIFT)] |= (Word)1<<((i)&~((Word)-1<<MPS_WORD_SHIFT)); \
|
||||
END
|
||||
|
||||
/* <design/bt#.if.res> */
|
||||
extern void (BTRes)(BT bt, Index index);
|
||||
#define BTRes(a, i) \
|
||||
BEGIN \
|
||||
(a)[((i)>>MPS_WORD_SHIFT)] &= \
|
||||
~((Word)1 << ((i) & ~((Word)-1<<MPS_WORD_SHIFT))); \
|
||||
END
|
||||
|
||||
|
||||
extern Bool BTCheck(BT bt);
|
||||
extern Res BTCreate(BT *btReturn, Arena arena, Count length);
|
||||
extern void BTDestroy(BT bt, Arena arena, Count length);
|
||||
|
||||
extern void BTSetRange(BT bt, Index base, Index limit);
|
||||
extern Bool BTIsSetRange(BT bt, Index base, Index limit);
|
||||
extern void BTResRange(BT bt, Index base, Index limit);
|
||||
extern Bool BTIsResRange(BT bt, Index base, Index limit);
|
||||
|
||||
extern Bool BTFindShortResRange(Index *baseReturn, Index *limitReturn,
|
||||
BT bt, Index searchBase, Index searchLimit,
|
||||
Count length);
|
||||
extern Bool BTFindShortResRangeHigh(Index *baseReturn, Index *limitReturn,
|
||||
BT bt, Index searchBase, Index searchLimit,
|
||||
Count length);
|
||||
extern Bool BTFindLongResRange(Index *baseReturn, Index *limitReturn,
|
||||
BT bt, Index searchBase, Index searchLimit,
|
||||
Count length);
|
||||
extern Bool BTFindLongResRangeHigh(Index *baseReturn, Index *limitReturn,
|
||||
BT bt, Index searchBase, Index searchLimit,
|
||||
Count length);
|
||||
|
||||
extern Bool BTRangesSame(BT BTx, BT BTy, Index base, Index limit);
|
||||
|
||||
extern void BTCopyInvertRange(BT fromBT, BT toBT, Index base, Index limit);
|
||||
extern void BTCopyRange(BT fromBT, BT toBT, Index base, Index limit);
|
||||
extern void BTCopyOffsetRange(BT fromBT, BT toBT,
|
||||
Index fromBase, Index fromLimit,
|
||||
Index toBase, Index toLimit);
|
||||
|
||||
extern Count BTCountResRange(BT bt, Index base, Index limit);
|
||||
|
||||
|
||||
#endif /* bt_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
599
mps/code/btcv.c
Normal file
599
mps/code/btcv.c
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
/* btss.c: BIT TABLE COVERAGE TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .readership: MPS developers
|
||||
*
|
||||
* .coverage: Direct coverage of BTFind*ResRange*, BTRangesSame,
|
||||
* BTISResRange, BTIsSetRange, BTCopyRange, BTCopyOffsetRange.
|
||||
* Reasonable coverage of BTCopyInvertRange, BTResRange,
|
||||
* BTSetRange, BTRes, BTSet, BTCreate, BTDestroy.
|
||||
*/
|
||||
|
||||
|
||||
#include "mpm.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
|
||||
SRCID(btcv, "$Id$");
|
||||
|
||||
|
||||
/* bt*Symmetric -- Symmetric operations on bit tables
|
||||
*
|
||||
* The operations take 2 bit tables, btlo & bthi.
|
||||
* They perform the equivalent BT* operation on btlo, and
|
||||
* a reflected operation on the bits of bthi from the opposite
|
||||
* direction.
|
||||
*/
|
||||
|
||||
#define btReflectIndex(btSize, i) (btSize - (i) - 1)
|
||||
#define btReflectLimit(btSize, i) (btSize - (i))
|
||||
|
||||
|
||||
static void btSetSymmetric(BT btlo, BT bthi, Count btSize, Index i)
|
||||
{
|
||||
BTSet(btlo, i);
|
||||
BTSet(bthi, btReflectIndex(btSize, i));
|
||||
}
|
||||
|
||||
static void btResSymmetric(BT btlo, BT bthi, Count btSize, Index i)
|
||||
{
|
||||
BTRes(btlo, i);
|
||||
BTRes(bthi, btReflectIndex(btSize, i));
|
||||
}
|
||||
|
||||
static void btSetRangeSymmetric(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit)
|
||||
{
|
||||
BTSetRange(btlo, base, limit);
|
||||
BTSetRange(bthi, btReflectLimit(btSize, limit), btReflectLimit(btSize, base));
|
||||
}
|
||||
|
||||
static void btResRangeSymmetric(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit)
|
||||
{
|
||||
BTResRange(btlo, base, limit);
|
||||
BTResRange(bthi, btReflectLimit(btSize, limit), btReflectLimit(btSize, base));
|
||||
}
|
||||
|
||||
|
||||
typedef Bool (*BTFinderFn)(Index *foundBase_o, Index *foundLimit_o,
|
||||
BT bt, Index base, Index limit, Count length);
|
||||
|
||||
|
||||
/* btTestSingleRange -- Test expectations for calls to BTFind*ResRange*
|
||||
*
|
||||
*/
|
||||
|
||||
static void btTestSingleRange(BTFinderFn finder, BT bt,
|
||||
Index base, Index limit,
|
||||
Count length,
|
||||
Bool expect,
|
||||
Index expectBase, Index expectLimit)
|
||||
{
|
||||
Bool found;
|
||||
Index foundBase, foundLimit;
|
||||
|
||||
found = finder(&foundBase, &foundLimit, bt, base, limit, length);
|
||||
cdie(found == expect, "FindResRange result");
|
||||
if (expect) {
|
||||
cdie(foundBase == expectBase, "FindResRange base");
|
||||
cdie(foundLimit == expectLimit, "FindResRange limit");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* btTestResRange -- Test expectations for calls to BTFindShortResRange
|
||||
*
|
||||
* Symmetrically call BTFindShortResRange / BTFindShortResRangeHigh
|
||||
* and test the expected results
|
||||
*/
|
||||
|
||||
static void btTestResRange(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Count length,
|
||||
Bool expect,
|
||||
Index expectBase, Index expectLimit)
|
||||
{
|
||||
btTestSingleRange(BTFindShortResRange, btlo,
|
||||
base, limit,
|
||||
length, expect,
|
||||
expectBase, expectLimit);
|
||||
|
||||
btTestSingleRange(BTFindShortResRangeHigh, bthi,
|
||||
btReflectLimit(btSize, limit),
|
||||
btReflectLimit(btSize, base),
|
||||
length, expect,
|
||||
btReflectLimit(btSize, expectLimit),
|
||||
btReflectLimit(btSize, expectBase));
|
||||
}
|
||||
|
||||
|
||||
/* btTestLongResRange -- Test expectations for calls to BTFindLongResRange
|
||||
*
|
||||
* Symmetrically call BTFindLongResRange / BTFindLongResRangeHigh
|
||||
* and test the expected results
|
||||
*/
|
||||
|
||||
static void btTestLongResRange(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Count length,
|
||||
Bool expect,
|
||||
Index expectBase, Index expectLimit)
|
||||
{
|
||||
btTestSingleRange(BTFindLongResRange, btlo,
|
||||
base, limit,
|
||||
length, expect,
|
||||
expectBase, expectLimit);
|
||||
|
||||
btTestSingleRange(BTFindLongResRangeHigh, bthi,
|
||||
btReflectLimit(btSize, limit),
|
||||
btReflectLimit(btSize, base),
|
||||
length, expect,
|
||||
btReflectLimit(btSize, expectLimit),
|
||||
btReflectLimit(btSize, expectBase));
|
||||
}
|
||||
|
||||
|
||||
/* btAllResTest -- tests with only a reset range
|
||||
*
|
||||
* Test finding reset ranges in an all-reset table.
|
||||
*/
|
||||
|
||||
static void btAllResTest(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Count length)
|
||||
{
|
||||
btResRangeSymmetric(btlo, bthi, btSize, 0, btSize);
|
||||
btTestResRange(btlo, bthi, btSize, base, limit, length,
|
||||
TRUE, base, base + length);
|
||||
btTestLongResRange(btlo, bthi, btSize, base, limit, length,
|
||||
TRUE, base, limit);
|
||||
}
|
||||
|
||||
|
||||
/* btNoResTest -- tests with no reset ranges
|
||||
*
|
||||
* Test finding reset ranges in an all-set search area of a table.
|
||||
* Reset the area outside the search to ensure it doesn't get found
|
||||
* by mistake.
|
||||
*/
|
||||
|
||||
static void btNoResTest(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Count length)
|
||||
{
|
||||
btResRangeSymmetric(btlo, bthi, btSize, 0, btSize);
|
||||
btSetRangeSymmetric(btlo, bthi, btSize, base, limit);
|
||||
btTestResRange(btlo, bthi, btSize, base, limit, length,
|
||||
FALSE, 0, 0);
|
||||
btTestLongResRange(btlo, bthi, btSize, base, limit, length,
|
||||
FALSE, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/* btResAndFindTest -- Test finding ranges of given size
|
||||
*
|
||||
* Resets the range between resBase & resLimit, and then attempts
|
||||
* to find it by searching in the range between base & limit.
|
||||
* Expect to find the range if it's long enough,
|
||||
*/
|
||||
|
||||
static void btResAndFindTest(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Index resBase, Index resLimit,
|
||||
Count length)
|
||||
{
|
||||
btResRangeSymmetric(btlo, bthi, btSize, resBase, resLimit);
|
||||
if ((resLimit - resBase) < length) {
|
||||
btTestResRange(btlo, bthi, btSize, base, limit, length,
|
||||
FALSE, 0, 0);
|
||||
btTestLongResRange(btlo, bthi, btSize, base, limit, length,
|
||||
FALSE, 0, 0);
|
||||
} else {
|
||||
btTestResRange(btlo, bthi, btSize, base, limit, length,
|
||||
TRUE, resBase, resBase + length);
|
||||
btTestLongResRange(btlo, bthi, btSize, base, limit, length,
|
||||
TRUE, resBase, resLimit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* btSingleResTest -- tests with a single reset range
|
||||
*
|
||||
* Test finding single ranges of various sizes
|
||||
*/
|
||||
|
||||
static void btSingleResTest(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Count length)
|
||||
{
|
||||
Count resLen;
|
||||
/* choose varying range lengths from too short to longer than needed */
|
||||
for (resLen = length - 1; resLen <= length + 1; resLen++) {
|
||||
if ((resLen > 0) && (resLen < (limit - base -2))) {
|
||||
/* place the ranges both near the beginning & near the end */
|
||||
/* of the search space */
|
||||
Index resBase, resLimit;
|
||||
for (resBase = base; resBase <= base +2; resBase++) {
|
||||
btResRangeSymmetric(btlo, bthi, btSize, 0, btSize);
|
||||
btSetRangeSymmetric(btlo, bthi, btSize, base, limit);
|
||||
btResAndFindTest(btlo, bthi, btSize, base, limit,
|
||||
resBase, resBase + resLen, length);
|
||||
}
|
||||
for (resLimit = limit; resLimit >= limit -2; resLimit--) {
|
||||
btResRangeSymmetric(btlo, bthi, btSize, 0, btSize);
|
||||
btSetRangeSymmetric(btlo, bthi, btSize, base, limit);
|
||||
btResAndFindTest(btlo, bthi, btSize, base, limit,
|
||||
resLimit - resLen, resLimit, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* btDoubleResTest -- Test finding double ranges of various sizes
|
||||
*
|
||||
* Set up 2 ranges with various relative positions. The first
|
||||
* range is always too small.
|
||||
*/
|
||||
|
||||
|
||||
/* Constants describing the type of arrangement of the 2 ranges */
|
||||
enum {
|
||||
ArrangeGAP1 = 0,
|
||||
ArrangeGAP2 = 1,
|
||||
ArrangeSPREAD = 2,
|
||||
ArrangeMAX
|
||||
};
|
||||
|
||||
typedef unsigned Arrangement;
|
||||
|
||||
/* Choose a limit for reset range 1 */
|
||||
static Index btArrangeRes1(Arrangement arrange,
|
||||
Index base, Index res2Base,
|
||||
Count length)
|
||||
{
|
||||
switch (arrange) {
|
||||
|
||||
case ArrangeGAP1: {
|
||||
/* Gap between ranges is of length 1 */
|
||||
return res2Base - 1;
|
||||
}
|
||||
|
||||
case ArrangeGAP2: {
|
||||
/* Gap between ranges is of length 2 */
|
||||
return res2Base - 2;
|
||||
}
|
||||
|
||||
case ArrangeSPREAD: {
|
||||
/* range 1 starts as far before range 2 as possible */
|
||||
return base + length;
|
||||
}
|
||||
|
||||
default:
|
||||
NOTREACHED;
|
||||
return 0; /* keep the compiler happy */
|
||||
}
|
||||
}
|
||||
|
||||
/* Constants describing the type of pattern for the first range */
|
||||
enum {
|
||||
PatternLEN1 = 0,
|
||||
PatternSETMID = 1,
|
||||
PatternJUSTSMALL = 2,
|
||||
PatternMAX
|
||||
};
|
||||
|
||||
typedef unsigned Pattern;
|
||||
|
||||
/* Choose a limit for reset range 1 */
|
||||
static void btResetFirstRange(BT btlo, BT bthi, Count btSize,
|
||||
Index res1Limit,
|
||||
Count length,
|
||||
Pattern pattern)
|
||||
{
|
||||
switch (pattern) {
|
||||
|
||||
case PatternLEN1: {
|
||||
/* First range is a single reset bit */
|
||||
btResSymmetric(btlo, bthi, btSize, res1Limit-1);
|
||||
return;
|
||||
}
|
||||
|
||||
case PatternSETMID: {
|
||||
/* Actually make 2 ranges here by setting a bit in the middle */
|
||||
Index mid = res1Limit - length + (length / 2);
|
||||
btResRangeSymmetric(btlo, bthi, btSize, res1Limit-length, res1Limit);
|
||||
btSetSymmetric(btlo, bthi, btSize, mid);
|
||||
return;
|
||||
}
|
||||
|
||||
case PatternJUSTSMALL: {
|
||||
/* Range of (length - 1) */
|
||||
btResRangeSymmetric(btlo, bthi, btSize,
|
||||
1 + res1Limit - length, res1Limit);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
NOTREACHED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void btDoubleResTest(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Count length)
|
||||
{
|
||||
Count res2Len;
|
||||
|
||||
if (length < 2)
|
||||
return; /* no possibility of making the first range too small */
|
||||
|
||||
/* choose varying range lengths for second res range */
|
||||
for (res2Len = length - 1; res2Len <= length + 1; res2Len++) {
|
||||
if ((res2Len > 0) && (res2Len < (limit - base -2))) {
|
||||
Index res2Limit;
|
||||
/* place the second ranges near the end of the search space */
|
||||
for (res2Limit = limit; res2Limit >= limit-8; res2Limit--) {
|
||||
Index res2Base = res2Limit - res2Len;
|
||||
Arrangement arrange;
|
||||
/* Pick one of a number of possible arrangements of the ranges */
|
||||
for (arrange = ArrangeGAP1; arrange < ArrangeMAX; arrange++) {
|
||||
Index res1Limit = btArrangeRes1(arrange, base, res2Base, length);
|
||||
Pattern pat;
|
||||
/* Pick one of a number of pattern types for range 1 */
|
||||
for (pat = PatternLEN1; pat < PatternMAX; pat++) {
|
||||
btResRangeSymmetric(btlo, bthi, btSize, 0, btSize);
|
||||
btSetRangeSymmetric(btlo, bthi, btSize, base, limit);
|
||||
btResetFirstRange(btlo, bthi, btSize, res1Limit, length, pat);
|
||||
/* Set up range 2 and expect to find it when searching */
|
||||
btResAndFindTest(btlo, bthi, btSize, base, limit,
|
||||
res2Base, res2Limit, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* btFindRangeTests -- Test BTFind*ResRange*
|
||||
*
|
||||
* Run a variety of FindResRange tests with different table patterns.
|
||||
*/
|
||||
|
||||
static void btFindRangeTests(BT btlo, BT bthi, Count btSize,
|
||||
Index base, Index limit,
|
||||
Count length)
|
||||
{
|
||||
btAllResTest(btlo, bthi, btSize, base, limit, length);
|
||||
btNoResTest(btlo, bthi, btSize, base, limit, length);
|
||||
btSingleResTest(btlo, bthi, btSize, base, limit, length);
|
||||
btDoubleResTest(btlo, bthi, btSize, base, limit, length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* btIsRangeTests -- Test BTIsResRange & BTIsSetRange
|
||||
*
|
||||
* Test ranges which are all reset or set apart from single
|
||||
* bits near to the base and limit (both inside and outside
|
||||
* the range).
|
||||
*
|
||||
* Test BTRangesSame by using the same bit patterns and comparing
|
||||
* with an appropriate all-set or all-reset table.
|
||||
*
|
||||
* These tests also test BTCopyInvertRange
|
||||
*/
|
||||
|
||||
static void btIsRangeTests(BT bt1, BT bt2, Count btSize,
|
||||
Index base, Index limit)
|
||||
{
|
||||
Index minBase, maxLimit, b, l;
|
||||
|
||||
if (base > 0) {
|
||||
minBase = base - 1;
|
||||
} else {
|
||||
minBase = 0;
|
||||
}
|
||||
|
||||
if (limit < btSize) {
|
||||
maxLimit = limit + 1;
|
||||
} else {
|
||||
maxLimit = btSize;
|
||||
}
|
||||
|
||||
for (b = minBase; b <= base+1; b++) {
|
||||
for (l = maxLimit; l >= limit-1; l--) {
|
||||
/* test a table which is all reset apart from a set bit */
|
||||
/* near each of the base and limit of the range in question */
|
||||
Bool outside; /* true if set bits are both outside test range */
|
||||
|
||||
outside = (b < base) && (l > limit);
|
||||
BTResRange(bt1, 0, btSize);
|
||||
BTSet(bt1, b);
|
||||
BTSet(bt1, l - 1);
|
||||
|
||||
/* invert the table for the inverse test */
|
||||
BTCopyInvertRange(bt1, bt2, 0, btSize);
|
||||
|
||||
/* Check it with BTIsResRange, and the inverse with BTIsSetRange */
|
||||
cdie(BTIsResRange(bt1, base, limit) == outside, "BTISResRange");
|
||||
cdie(BTIsSetRange(bt2, base, limit) == outside, "BTISSetRange");
|
||||
|
||||
/* Check the same range with BTRangesSame on an empty table */
|
||||
BTResRange(bt2, 0, btSize);
|
||||
cdie(BTRangesSame(bt1, bt2, base, limit) == outside, "BTRangeSame");
|
||||
|
||||
/* Check the inverse with BTRangesSame on a full table */
|
||||
BTCopyInvertRange(bt1, bt2, 0, btSize);
|
||||
BTSetRange(bt1, 0, btSize);
|
||||
cdie(BTRangesSame(bt1, bt2, base, limit) == outside, "BTRangeSame");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* btCopyTests -- Test BTCopyRange & BTCopyOffsetRange
|
||||
*
|
||||
* Test copying ranges which are all reset or set apart from
|
||||
* single bits near to the base and limit (both inside and outside
|
||||
* the range).
|
||||
*
|
||||
*/
|
||||
|
||||
static void btCopyTests(BT bt1, BT bt2, Count btSize,
|
||||
Index base, Index limit)
|
||||
{
|
||||
Index minBase, maxLimit, b, l;
|
||||
|
||||
if (base > 0) {
|
||||
minBase = base - 1;
|
||||
} else {
|
||||
minBase = 0;
|
||||
}
|
||||
|
||||
if (limit < btSize) {
|
||||
maxLimit = limit + 1;
|
||||
} else {
|
||||
maxLimit = btSize;
|
||||
}
|
||||
|
||||
for (b = minBase; b <= base+1; b++) {
|
||||
for (l = maxLimit; l >= limit-1; l--) {
|
||||
/* initialize a table which is all reset apart from a set bit */
|
||||
/* near each of the base and limit of the range in question */
|
||||
Bool outside; /* true if set bits are both outside test range */
|
||||
|
||||
outside = (b < base) && (l > limit);
|
||||
BTResRange(bt1, 0, btSize);
|
||||
BTSet(bt1, b);
|
||||
BTSet(bt1, l - 1);
|
||||
|
||||
/* check copying the region to the bottom of the other table */
|
||||
BTCopyOffsetRange(bt1, bt2, base, limit, 0, limit - base);
|
||||
cdie(BTIsResRange(bt2, 0, limit - base) == outside, "BTIsResRange");
|
||||
|
||||
/* check copying the region to the top of the other table */
|
||||
BTCopyOffsetRange(bt1, bt2,
|
||||
base, limit, btSize + base - limit, btSize);
|
||||
cdie(BTIsResRange(bt2, btSize + base - limit, btSize) == outside,
|
||||
"BTIsResRange");
|
||||
|
||||
/* check copying the region to the same place in the other table */
|
||||
BTCopyOffsetRange(bt1, bt2, base, limit, base, limit);
|
||||
cdie(BTIsResRange(bt2, base, limit) == outside, "BTIsResRange");
|
||||
|
||||
/* copy the range and check its the same */
|
||||
BTCopyRange(bt1, bt2, base, limit);
|
||||
cdie(BTRangesSame(bt1, bt2, base, limit), "BTRangeSame");
|
||||
|
||||
/* invert the table, then copy it and check it again */
|
||||
BTCopyInvertRange(bt2, bt1, 0, btSize);
|
||||
BTCopyRange(bt1, bt2, base, limit);
|
||||
cdie(BTRangesSame(bt1, bt2, base, limit), "BTRangeSame");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* btTests -- Do all the tests
|
||||
*/
|
||||
|
||||
static void btTests(BT btlo, BT bthi, Count btSize)
|
||||
{
|
||||
Index base, limit;
|
||||
|
||||
/* Perform lots of tests over different subranges */
|
||||
for (base = 0; base < MPS_WORD_WIDTH; base++) {
|
||||
for (limit = btSize; limit > (btSize-MPS_WORD_WIDTH); limit--) {
|
||||
/* Perform Is*Range tests over those subranges */
|
||||
btIsRangeTests(btlo, bthi, btSize, base, limit);
|
||||
|
||||
/* Perform Copy*Range tests over those subranges */
|
||||
btCopyTests(btlo, bthi, btSize, base, limit);
|
||||
|
||||
/* Perform FindResRange tests with different lengths */
|
||||
btFindRangeTests(btlo, bthi, btSize, base, limit, 1);
|
||||
btFindRangeTests(btlo, bthi, btSize, base, limit, 2);
|
||||
btFindRangeTests(btlo, bthi, btSize, base, limit, MPS_WORD_WIDTH - 1);
|
||||
btFindRangeTests(btlo, bthi, btSize, base, limit, MPS_WORD_WIDTH);
|
||||
btFindRangeTests(btlo, bthi, btSize, base, limit, MPS_WORD_WIDTH + 1);
|
||||
btFindRangeTests(btlo, bthi, btSize, base, limit, limit - base -1);
|
||||
btFindRangeTests(btlo, bthi, btSize, base, limit, limit - base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Start the world */
|
||||
|
||||
#define testArenaSIZE (((size_t)64)<<20)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t mpsArena;
|
||||
Arena arena; /* the ANSI arena which we use to allocate the BT */
|
||||
BT btlo, bthi;
|
||||
Count btSize;
|
||||
|
||||
/* tests need 4 whole words plus a few extra bits */
|
||||
btSize = MPS_WORD_WIDTH * 4 + 10;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
die(mps_arena_create(&mpsArena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"mps_arena_create");
|
||||
arena = (Arena)mpsArena; /* avoid pun */
|
||||
|
||||
die((mps_res_t)BTCreate(&btlo, arena, btSize),
|
||||
"failed to create low bit table");
|
||||
|
||||
die((mps_res_t)BTCreate(&bthi, arena, btSize),
|
||||
"failed to create high bit table");
|
||||
|
||||
btTests(btlo, bthi, btSize);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
405
mps/code/bttest.c
Normal file
405
mps/code/bttest.c
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/* bttest.c: BIT TABLE TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*/
|
||||
|
||||
|
||||
#include "mpm.h"
|
||||
#include "mps.h"
|
||||
#include "mpsavm.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpstd.h"
|
||||
|
||||
#include <stdio.h> /* fflush, fgets, printf, putchar, puts */
|
||||
#include <stdlib.h> /* exit, strtol */
|
||||
|
||||
SRCID(bttest, "$Id$");
|
||||
|
||||
|
||||
static BT bt; /* the BT which we will use */
|
||||
static Size btSize; /* the size of the current BT */
|
||||
static Arena arena; /* the arena which we use to allocate the BT */
|
||||
|
||||
|
||||
#define MAX_ARGS 3
|
||||
|
||||
|
||||
static Word args[MAX_ARGS];
|
||||
static Count argCount;
|
||||
|
||||
|
||||
static Bool argInRange(Index arg)
|
||||
{
|
||||
if (bt == NULL) {
|
||||
printf("no BT\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (args[arg] >= btSize) {
|
||||
printf("out of range\n");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static Bool checkDefaultRange(Index arg)
|
||||
{
|
||||
if (bt == NULL) {
|
||||
printf("no BT\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (argCount == arg+1) {
|
||||
printf("range half-specified\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (argCount == arg) { /* use default range */
|
||||
args[arg] = 0;
|
||||
args[arg+1] = btSize;
|
||||
return TRUE;
|
||||
}
|
||||
if (args[arg] >= args[arg+1]) {
|
||||
printf("range ill-formed\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (args[arg+1] > btSize) {
|
||||
printf("range too high\n");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE; /* explicit valid range */
|
||||
}
|
||||
|
||||
|
||||
static void quit(void)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
static void destroy(void)
|
||||
{
|
||||
if (bt != NULL) {
|
||||
BTDestroy(bt, arena, btSize);
|
||||
bt = NULL;
|
||||
} else {
|
||||
printf("No BT to destroy\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void create(void)
|
||||
{
|
||||
Res res;
|
||||
if (args[0] < 1) {
|
||||
printf("can't create a BT of size 0\n");
|
||||
return;
|
||||
}
|
||||
if (bt != NULL)
|
||||
destroy();
|
||||
res = BTCreate(&bt, arena, args[0]);
|
||||
if (res == ResOK) {
|
||||
btSize = args[0];
|
||||
BTResRange(bt, 0, btSize);
|
||||
} else {
|
||||
printf("BTCreate returned %d\n",res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void set(void)
|
||||
{
|
||||
if (argInRange(0))
|
||||
(BTSet)(bt, args[0]);
|
||||
}
|
||||
|
||||
|
||||
static void reset(void)
|
||||
{
|
||||
if (argInRange(0))
|
||||
(BTRes)(bt, args[0]);
|
||||
}
|
||||
|
||||
|
||||
static void get(void)
|
||||
{
|
||||
if (argInRange(0)) {
|
||||
Bool b = (BTGet)(bt, args[0]);
|
||||
puts(b ? "TRUE" : "FALSE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void setRange(void)
|
||||
{
|
||||
if (checkDefaultRange(0))
|
||||
BTSetRange(bt, args[0], args[1]);
|
||||
}
|
||||
|
||||
|
||||
static void resetRange(void)
|
||||
{
|
||||
if (checkDefaultRange(0))
|
||||
BTResRange(bt, args[0], args[1]);
|
||||
}
|
||||
|
||||
|
||||
static void isSetRange(void)
|
||||
{
|
||||
if (checkDefaultRange(0)) {
|
||||
Bool b = BTIsSetRange(bt, args[0], args[1]);
|
||||
puts(b ? "TRUE" : "FALSE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void isResRange(void)
|
||||
{
|
||||
if (checkDefaultRange(0)) {
|
||||
Bool b = BTIsResRange(bt, args[0], args[1]);
|
||||
puts(b ? "TRUE" : "FALSE");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void findShortResRange(void)
|
||||
{
|
||||
if (checkDefaultRange(1)) {
|
||||
if (args[0] > (args[2] - args[1])) {
|
||||
printf("can't fit length in range\n");
|
||||
} else {
|
||||
Index base, limit;
|
||||
Bool b = BTFindShortResRange(&base, &limit, bt,
|
||||
args[1], args[2], args[0]);
|
||||
if (b)
|
||||
printf("%"PRIuLONGEST" - %"PRIuLONGEST"\n",
|
||||
(ulongest_t)base, (ulongest_t)limit);
|
||||
else
|
||||
printf("FALSE\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void findShortResRangeHigh(void)
|
||||
{
|
||||
if (checkDefaultRange(1)) {
|
||||
if (args[0] > (args[2] - args[1])) {
|
||||
printf("can't fit length in range\n");
|
||||
} else {
|
||||
Index base, limit;
|
||||
Bool b = BTFindShortResRangeHigh(&base, &limit, bt,
|
||||
args[1], args[2], args[0]);
|
||||
if (b)
|
||||
printf("%"PRIuLONGEST" - %"PRIuLONGEST"\n",
|
||||
(ulongest_t)base, (ulongest_t)limit);
|
||||
else
|
||||
printf("FALSE\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void findLongResRange(void)
|
||||
{
|
||||
if (checkDefaultRange(1)) {
|
||||
if (args[0] > (args[2] - args[1])) {
|
||||
printf("can't fit length in range\n");
|
||||
} else {
|
||||
Index base, limit;
|
||||
Bool b = BTFindLongResRange(&base, &limit, bt,
|
||||
args[1], args[2], args[0]);
|
||||
if (b)
|
||||
printf("%"PRIuLONGEST" - %"PRIuLONGEST"\n",
|
||||
(ulongest_t)base, (ulongest_t)limit);
|
||||
else
|
||||
printf("FALSE\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void help(void)
|
||||
{
|
||||
printf("c <s> create a BT of size 's'\n"
|
||||
"d destroy the current BT\n"
|
||||
"s <i> set the bit index 'i'\n"
|
||||
"r <i> reset the bit index 'i'\n"
|
||||
"g <i> get the bit index 'i'\n");
|
||||
printf("sr [<i> <i>] set the specified range\n"
|
||||
"rr [<i> <i>] reset the specified range\n"
|
||||
"is [<i> <i>] is the specified range set?\n"
|
||||
"ir [<i> <i>] is the specified range reset?\n");
|
||||
printf("f <l> [<i> <i>] find a reset range of length 'l'.\n"
|
||||
"fh <l> [<i> <i>] find a reset range length 'l', working downwards\n"
|
||||
"fl <l> [<i> <i>] find a reset range of length at least 'l'\n"
|
||||
"q quit\n"
|
||||
"? print this message\n");
|
||||
printf("\n"
|
||||
"No way of testing BTSize, BTRangesSame, or BTCopyInvertRange.\n");
|
||||
}
|
||||
|
||||
|
||||
static struct commandShapeStruct {
|
||||
const char *name;
|
||||
Count min_args;
|
||||
Count max_args;
|
||||
void (*fun)(void);
|
||||
} commandShapes[] = {
|
||||
{"c", 1, 1, create},
|
||||
{"d", 0, 0, destroy},
|
||||
{"s", 1, 1, set},
|
||||
{"r", 1, 1, reset},
|
||||
{"g", 1, 1, get},
|
||||
{"sr", 0, 2, setRange},
|
||||
{"rr", 0, 2, resetRange},
|
||||
{"is", 0, 2, isSetRange},
|
||||
{"ir", 0, 2, isResRange},
|
||||
{"f", 1, 3, findShortResRange},
|
||||
{"fh", 1, 3, findShortResRangeHigh},
|
||||
{"fl", 1, 3, findLongResRange},
|
||||
{"?", 0, 0, help},
|
||||
{"q", 0, 0, quit},
|
||||
{ NULL, 0, 0, NULL}
|
||||
};
|
||||
|
||||
|
||||
typedef struct commandShapeStruct *commandShape;
|
||||
|
||||
|
||||
static void obeyCommand(const char *command)
|
||||
{
|
||||
commandShape shape = commandShapes;
|
||||
while(shape->name != NULL) {
|
||||
const char *csp = shape->name;
|
||||
const char *p = command;
|
||||
while (*csp == *p) {
|
||||
csp++;
|
||||
p++;
|
||||
}
|
||||
if ((*csp == 0) && ((*p == '\n') || (*p == ' '))) { /* complete match */
|
||||
argCount = 0;
|
||||
while ((*p == ' ') && (argCount < shape->max_args)) {
|
||||
/* get an argument */
|
||||
char *newP;
|
||||
long l;
|
||||
l = strtol(p, &newP, 0);
|
||||
if(l < 0) { /* negative integer */
|
||||
printf("negative integer arguments are invalid\n");
|
||||
return;
|
||||
}
|
||||
args[argCount] = (unsigned long)l;
|
||||
if (newP == p) { /* strtoul failed */
|
||||
printf("couldn't parse an integer argument\n");
|
||||
return;
|
||||
}
|
||||
p = newP;
|
||||
++ argCount;
|
||||
}
|
||||
if (argCount < shape->min_args) {
|
||||
printf("insufficient arguments to command\n");
|
||||
} else if (*p != '\n') {
|
||||
printf("too many arguments to command\n");
|
||||
} else { /* do the command */
|
||||
shape->fun();
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
++ shape; /* try next command */
|
||||
}
|
||||
}
|
||||
printf("command not understood\n");
|
||||
help();
|
||||
}
|
||||
|
||||
|
||||
static void showBT(void)
|
||||
{
|
||||
Index i;
|
||||
char c;
|
||||
if (bt == NULL)
|
||||
return;
|
||||
i = 0;
|
||||
while((i < btSize) && (i < 50)) {
|
||||
if (i % 10 == 0)
|
||||
c = (char)(((i / 10) % 10) + '0');
|
||||
else
|
||||
c = ' ';
|
||||
putchar(c);
|
||||
++ i;
|
||||
}
|
||||
putchar('\n');
|
||||
i = 0;
|
||||
while((i < btSize) && (i < 50)) {
|
||||
c = (char)((i % 10) +'0');
|
||||
putchar(c);
|
||||
++ i;
|
||||
}
|
||||
putchar('\n');
|
||||
i = 0;
|
||||
while(i < btSize) {
|
||||
if (BTGet(bt,i))
|
||||
c = 'O';
|
||||
else
|
||||
c = '.';
|
||||
putchar(c);
|
||||
++ i;
|
||||
if (i % 50 == 0)
|
||||
putchar('\n');
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
|
||||
#define testArenaSIZE (((size_t)64)<<20)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bt = NULL;
|
||||
btSize = 0;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
die(mps_arena_create((mps_arena_t*)&arena, mps_arena_class_vm(),
|
||||
testArenaSIZE),
|
||||
"mps_arena_create");
|
||||
while(1) {
|
||||
char input[100];
|
||||
printf("bt test> ");
|
||||
(void)fflush(stdout);
|
||||
if (fgets(input, 100, stdin)) {
|
||||
obeyCommand(input);
|
||||
showBT();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
1354
mps/code/buffer.c
Normal file
1354
mps/code/buffer.c
Normal file
File diff suppressed because it is too large
Load diff
1248
mps/code/cbs.c
Normal file
1248
mps/code/cbs.c
Normal file
File diff suppressed because it is too large
Load diff
85
mps/code/cbs.h
Normal file
85
mps/code/cbs.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* cbs.h: CBS -- Coalescing Block Structure
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .source: <design/cbs>.
|
||||
*/
|
||||
|
||||
#ifndef cbs_h
|
||||
#define cbs_h
|
||||
|
||||
#include "arg.h"
|
||||
#include "mpmtypes.h"
|
||||
#include "mpm.h"
|
||||
#include "mpmst.h"
|
||||
#include "rangetree.h"
|
||||
#include "splay.h"
|
||||
|
||||
typedef struct CBSFastBlockStruct *CBSFastBlock;
|
||||
typedef struct CBSFastBlockStruct {
|
||||
struct RangeTreeStruct rangeTreeStruct;
|
||||
Size maxSize; /* accurate maximum block size of sub-tree */
|
||||
} CBSFastBlockStruct;
|
||||
|
||||
typedef struct CBSZonedBlockStruct *CBSZonedBlock;
|
||||
typedef struct CBSZonedBlockStruct {
|
||||
struct CBSFastBlockStruct cbsFastBlockStruct;
|
||||
ZoneSet zones; /* union zone set of all ranges in sub-tree */
|
||||
} CBSZonedBlockStruct;
|
||||
|
||||
typedef struct CBSStruct *CBS, *CBSFast, *CBSZoned;
|
||||
|
||||
extern Bool CBSCheck(CBS cbs);
|
||||
|
||||
|
||||
/* CBSLand -- convert CBS to Land
|
||||
*
|
||||
* We would like to use MustBeA(Land, cbs) for this, but it produces
|
||||
* bogus warnings about strict aliasing from GCC 4.7 (and probably
|
||||
* 4.8). We can abolish this macro when those are no longer in use in
|
||||
* MPS development.
|
||||
*/
|
||||
|
||||
#define CBSLand(cbs) (&(cbs)->landStruct)
|
||||
|
||||
|
||||
DECLARE_CLASS(Land, CBS, Land);
|
||||
DECLARE_CLASS(Land, CBSFast, CBS);
|
||||
DECLARE_CLASS(Land, CBSZoned, CBSFast);
|
||||
|
||||
extern const struct mps_key_s _mps_key_cbs_block_pool;
|
||||
#define CBSBlockPool (&_mps_key_cbs_block_pool)
|
||||
#define CBSBlockPool_FIELD pool
|
||||
|
||||
#endif /* cbs_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
402
mps/code/check.h
Normal file
402
mps/code/check.h
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
/* check.h: ASSERTION INTERFACE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*
|
||||
* .aver: This header defines a family of AVER and NOTREACHED macros.
|
||||
* These macros should be used to instrument and annotate code with
|
||||
* invariants, and so provide both interface and internal consistency
|
||||
* checks.
|
||||
*
|
||||
* .comment: Non-obvious AVER statements should always be accompanied by
|
||||
* a comment.
|
||||
*
|
||||
* .disable: When assertions are disabled, AVER expands to something
|
||||
* which contains the condition but discards the result. Compilers
|
||||
* will throw the code away, but check its syntax.
|
||||
*
|
||||
* .trans.level-check: CheckLevel itself is not checked anywhere.
|
||||
*
|
||||
* .careful: BE CAREFUL when changing this file. It is easy to make mistakes
|
||||
* and change the checking level in a variety and thereby its performance
|
||||
* without realising it. This has happened before. Eyeball the preprocessor
|
||||
* output for each variety. For example:
|
||||
*
|
||||
* cc -E -DCONFIG_VAR_RASH trace.c
|
||||
* cc -E -DCONFIG_VAR_HOT trace.c
|
||||
* cc -E -DCONFIG_VAR_COOL trace.c
|
||||
*
|
||||
* Then look at TraceCheck to make sure checking is right, TraceAddWhite
|
||||
* for general assertions, and TraceFix for the critical path assertions.
|
||||
*/
|
||||
|
||||
#ifndef check_h
|
||||
#define check_h
|
||||
|
||||
#include "config.h"
|
||||
#include "misc.h"
|
||||
#include "mpslib.h"
|
||||
#include "protocol.h"
|
||||
|
||||
|
||||
/* ASSERT -- basic assertion
|
||||
*
|
||||
* The ASSERT macro is equivalent to the ISO C assert() except that it is
|
||||
* always defined, and uses the assertion handler from the MPS plinth, which
|
||||
* can be replaced by the client code.
|
||||
*
|
||||
* It is not intended for direct use within the MPS. Use AVER and CHECK
|
||||
* macros, which can be controlled by both build and run-time configuration.
|
||||
*/
|
||||
|
||||
#define ASSERT(cond, condstring) \
|
||||
BEGIN \
|
||||
if (LIKELY(cond)) NOOP; else \
|
||||
mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)); \
|
||||
END
|
||||
|
||||
#define ASSERTP(cond, condstring, default_) \
|
||||
((void)(LIKELY(cond) \
|
||||
|| (mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)), FALSE)), \
|
||||
(default_))
|
||||
|
||||
#define ASSERT_ISTYPE(type, val) (type ## Check(val))
|
||||
#define ASSERT_TYPECHECK(type, val) \
|
||||
ASSERT(ASSERT_ISTYPE(type, val), "TypeCheck " #type ": " #val)
|
||||
|
||||
#define ASSERT_ISCLASS(klass, val) (klass ## Check(CouldBeA(klass, val)))
|
||||
#define ASSERT_CLASSCHECK(klass, val) \
|
||||
ASSERT(ASSERT_ISCLASS(klass, val), "ClassCheck " #klass ": " #val)
|
||||
|
||||
#define ASSERT_NULLCHECK(type, val) \
|
||||
ASSERT((val) != NULL, "NullCheck " #type ": " #val)
|
||||
|
||||
|
||||
/* CheckLevel -- control for check method behaviour
|
||||
*
|
||||
* When the MPS is build with AVER_AND_CHECK_ALL (in a "cool" variety) the
|
||||
* static variable CheckLevel controls the frequency and detail of
|
||||
* consistency checking on structures.
|
||||
*
|
||||
* By default, CHECKLEVEL is defined to a static value in config.h, though
|
||||
* it can be overridden on the compiler command line, e.g.
|
||||
* cc -DCHECKLEVEL=CheckLevelSHALLOW ...
|
||||
*
|
||||
* However, if CHECKLEVEL_DYNAMIC is defined we use a variable to control
|
||||
* the level of checking. The run-time overhead for this is quite high
|
||||
* (observed double run-time on amcss when the variable is set to SHALLOW).
|
||||
* CHECKLEVEL_DYNAMIC should be set to the initial level for the variable,
|
||||
* which is in mpm.c.
|
||||
*
|
||||
* In general, it's better to adjust the check level by defining CHECKLEVEL
|
||||
* but this is intended to meet the case where a run-time adjustable
|
||||
* checking level is required -- where recompilation or relinking is
|
||||
* undesirable or impossible.
|
||||
*
|
||||
* TODO: Should also allow the check level variable to come from an
|
||||
* environment variable.
|
||||
*/
|
||||
|
||||
enum {
|
||||
CheckLevelMINIMAL = 0, /* local sig check only */
|
||||
CheckLevelSHALLOW = 1, /* local invariants, */
|
||||
/* and adjacent (up, down) sig checks */
|
||||
CheckLevelDEEP = 2 /* local invariants, */
|
||||
/* and adjacent up sig checks */
|
||||
/* and recursive down full type checks */
|
||||
};
|
||||
|
||||
#ifdef CHECKLEVEL_DYNAMIC
|
||||
extern unsigned CheckLevel;
|
||||
#undef CHECKLEVEL
|
||||
#define CHECKLEVEL CheckLevel
|
||||
#endif
|
||||
|
||||
|
||||
/* AVER, AVERT, AVERC, AVERP -- MPM assertions
|
||||
*
|
||||
* AVER and friends are used to assert conditions in the code.
|
||||
*
|
||||
* AVER checks an expression.
|
||||
*
|
||||
* AVERT checks that a value is of the correct type and may perform
|
||||
* consistency checks on the value by calling a check function.
|
||||
*
|
||||
* AVERC checks that a value is of the correct class (including
|
||||
* subclasses) and may perform consistency checks on the value by
|
||||
* calling a check function.
|
||||
*
|
||||
* AVERP checks an expression but is itself a void * expression, and
|
||||
* so can be used in expression macros.
|
||||
*
|
||||
* AVER etc. are on by default, and check conditions even in "hot"
|
||||
* varieties intended to work in production. To avoid the cost of a
|
||||
* check in critical parts of the code, use AVER_CRITICAL etc., but
|
||||
* only when you've *proved* that this makes a difference to
|
||||
* performance that affects requirements.
|
||||
*/
|
||||
|
||||
#if defined(AVER_AND_CHECK_NONE)
|
||||
|
||||
#define AVER(cond) DISCARD(cond)
|
||||
#define AVERT(type, val) DISCARD(ASSERT_ISTYPE(type, val))
|
||||
#define AVERC(klass, val) DISCARD(ASSERT_ISCLASS(klass, val))
|
||||
#define AVERP(cond, dflt) (DISCARD_EXP(cond), dflt)
|
||||
#define AVERPC(cond, condstring, dflt) (DISCARD_EXP(cond), dflt)
|
||||
|
||||
#else
|
||||
|
||||
#define AVER(cond) ASSERT(cond, #cond)
|
||||
#define AVERT ASSERT_TYPECHECK
|
||||
#define AVERC ASSERT_CLASSCHECK
|
||||
#define AVERP(cond, dflt) ASSERTP(cond, #cond, dflt)
|
||||
#define AVERPC ASSERTP
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AVER_AND_CHECK_ALL)
|
||||
|
||||
#define AVER_CRITICAL(cond) ASSERT(cond, #cond)
|
||||
#define AVERT_CRITICAL ASSERT_TYPECHECK
|
||||
#define AVERC_CRITICAL ASSERT_CLASSCHECK
|
||||
#define AVERP_CRITICAL(cond, dflt) ASSERTP(cond, #cond, dflt)
|
||||
#define AVERPC_CRITICAL ASSERTP
|
||||
|
||||
#else
|
||||
|
||||
#define AVER_CRITICAL DISCARD
|
||||
#define AVERT_CRITICAL(type, val) DISCARD(ASSERT_ISTYPE(type, val))
|
||||
#define AVERC_CRITICAL(klass, val) DISCARD(ASSERT_ISCLASS(klass, val))
|
||||
#define AVERP_CRITICAL(cond, dflt) (DISCARD_EXP(cond), dflt)
|
||||
#define AVERPC_CRITICAL(cond, condstring, dflt) (DISCARD_EXP(cond), dflt)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* NOTREACHED -- control should never reach this statement
|
||||
*
|
||||
* This is a sort of AVER; it is equivalent to AVER(FALSE), but will produce
|
||||
* a more informative message.
|
||||
*/
|
||||
|
||||
#if defined(AVER_AND_CHECK_NONE)
|
||||
|
||||
#define NOTREACHED NOOP
|
||||
|
||||
#else
|
||||
|
||||
#define NOTREACHED \
|
||||
BEGIN \
|
||||
mps_lib_assert_fail(__FILE__, __LINE__, "unreachable code"); \
|
||||
END
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* TESTT -- check type simply
|
||||
*
|
||||
* Must be thread safe. <design/interface-c#.thread-safety>
|
||||
* and <design/interface-c#.check.space>.
|
||||
*/
|
||||
|
||||
#define TESTT(type, val) ((val) != NULL && (val)->sig == type ## Sig)
|
||||
|
||||
|
||||
/* TESTC -- check class simply
|
||||
*
|
||||
* TODO: Does this need to be thread safe like TESTT?
|
||||
*/
|
||||
|
||||
#define TESTC(klass, val) ((val) != NULL && IsA(klass, val))
|
||||
|
||||
|
||||
/* CHECKS, CHECKC -- Check Signature, Check Class
|
||||
*
|
||||
* (if CHECKLEVEL == CheckLevelMINIMAL, this is all we check)
|
||||
*/
|
||||
|
||||
#if defined(AVER_AND_CHECK_NONE)
|
||||
#define CHECKS(type, val) DISCARD(TESTT(type, val))
|
||||
#define CHECKC(klass, val) DISCARD(MustBeA(klass, val))
|
||||
#else
|
||||
#define CHECKS(type, val) \
|
||||
ASSERT(TESTT(type, val), "SigCheck " #type ": " #val)
|
||||
#define CHECKC(klass, val) \
|
||||
ASSERT(TESTC(klass, val), "ClassCheck " #klass ": " #val)
|
||||
#endif
|
||||
|
||||
|
||||
/* CHECKL, CHECKD, CHECKU -- local, "down", and "up" checks
|
||||
*
|
||||
* Each type should have a function defined called <type>Check that checks
|
||||
* the consistency of the type. This function should return TRUE iff the
|
||||
* value passes consistency checks. In general, it should assert otherwise,
|
||||
* but we allow for the possibility of returning FALSE in this case for
|
||||
* configuration adaptability.
|
||||
*
|
||||
* For structure types, the check function should:
|
||||
*
|
||||
* - check its own signature with CHECKS
|
||||
*
|
||||
* - check fields that it "owns" with CHECKL, like asserts
|
||||
*
|
||||
* - check "down" values which are its "children" with CHECKD
|
||||
*
|
||||
* - check "up" values which are its "parents" with CHECKU.
|
||||
*
|
||||
* These various checks will be compiled out or compiled to be controlled
|
||||
* by CHECKLEVEL.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* Bool MessageCheck(Message message)
|
||||
* {
|
||||
* CHECKS(Message, message);
|
||||
* CHECKU(Arena, message->arena);
|
||||
* CHECKD(MessageClass, message->class);
|
||||
* CHECKL(RingCheck(&message->queueRing));
|
||||
* CHECKL(MessageIsClocked(message) || (message->postedClock == 0));
|
||||
* return TRUE;
|
||||
* }
|
||||
*
|
||||
* The parent/child distinction depends on the structure, but in the MPS
|
||||
* the Arena has no parents, and has children which are Pools, which have
|
||||
* children which are Segments, etc.
|
||||
*
|
||||
* The important thing is to have a partial order on types so that recursive
|
||||
* checking will terminate. When CHECKLEVEL is set to DEEP, checking will
|
||||
* recurse into check methods for children, but will only do a shallow
|
||||
* signature check on parents, avoiding infinite regression.
|
||||
*/
|
||||
|
||||
#if defined(AVER_AND_CHECK_ALL)
|
||||
|
||||
#define CHECK_BY_LEVEL(minimal, shallow, deep) \
|
||||
BEGIN \
|
||||
switch (CHECKLEVEL) { \
|
||||
case CheckLevelDEEP: deep; break; \
|
||||
case CheckLevelSHALLOW: shallow; break; \
|
||||
default: NOTREACHED; /* fall through */ \
|
||||
case CheckLevelMINIMAL: minimal; break; \
|
||||
} \
|
||||
END
|
||||
|
||||
#define CHECKL(cond) \
|
||||
CHECK_BY_LEVEL(NOOP, \
|
||||
ASSERT(cond, #cond), \
|
||||
ASSERT(cond, #cond))
|
||||
|
||||
#define CHECKD(type, val) \
|
||||
CHECK_BY_LEVEL(NOOP, \
|
||||
CHECKS(type, val), \
|
||||
ASSERT_TYPECHECK(type, val))
|
||||
|
||||
#define CHECKD_NOSIG(type, val) \
|
||||
CHECK_BY_LEVEL(NOOP, \
|
||||
ASSERT_NULLCHECK(type, val), \
|
||||
ASSERT_TYPECHECK(type, val))
|
||||
|
||||
#define CHECKD_CLASS(klass, val) \
|
||||
CHECK_BY_LEVEL(NOOP, \
|
||||
CHECKC(klass, val) \
|
||||
ASSERT_CLASSCHECK(klass, val))
|
||||
|
||||
#define CHECKU(type, val) \
|
||||
CHECK_BY_LEVEL(NOOP, \
|
||||
CHECKS(type, val), \
|
||||
CHECKS(type, val))
|
||||
|
||||
#define CHECKU_NOSIG(type, val) \
|
||||
CHECK_BY_LEVEL(NOOP, \
|
||||
ASSERT_NULLCHECK(type, val), \
|
||||
ASSERT_NULLCHECK(type, val))
|
||||
|
||||
#else /* AVER_AND_CHECK_ALL, not */
|
||||
|
||||
/* TODO: This gives comparable performance to RASH when compiling
|
||||
using mps.c and -O2 (to get check methods inlined), but is it a bit
|
||||
too minimal? How much do we rely on check methods? */
|
||||
|
||||
#define CHECKL(cond) DISCARD(cond)
|
||||
#define CHECKD(type, val) DISCARD(TESTT(type, val))
|
||||
#define CHECKD_NOSIG(type, val) DISCARD((val) != NULL)
|
||||
#define CHECKD_CLASS(klass, val) DISCARD((val) != NULL)
|
||||
#define CHECKU(type, val) DISCARD(TESTT(type, val))
|
||||
#define CHECKU_NOSIG(type, val) DISCARD((val) != NULL)
|
||||
|
||||
#endif /* AVER_AND_CHECK_ALL */
|
||||
|
||||
|
||||
/* COMPAT* -- type compatibility checking
|
||||
*
|
||||
* .check.macros: The COMPAT* macros use some C trickery to attempt to
|
||||
* verify that certain types and fields are equivalent. They do not do
|
||||
* a complete job. This trickery is justified by the security gained
|
||||
* in knowing that <code/mps.h> matches the MPM. See
|
||||
* <design/interface-c#.check.types>. [This paragraph is intended to
|
||||
* satisfy rule.impl.trick.]
|
||||
*/
|
||||
|
||||
/* compile-time check */
|
||||
#define COMPATLVALUE(lv1, lv2) \
|
||||
((void)sizeof((lv1) = (lv2)), (void)sizeof((lv2) = (lv1)), TRUE)
|
||||
|
||||
/* aims to test whether t1 and t2 are assignment-compatible */
|
||||
#define COMPATTYPE(t1, t2) \
|
||||
(sizeof(t1) == sizeof(t2) && \
|
||||
COMPATLVALUE(*((t1 *)0), *((t2 *)0)))
|
||||
|
||||
#define COMPATFIELDAPPROX(s1, f1, s2, f2) \
|
||||
(sizeof(((s1 *)0)->f1) == sizeof(((s2 *)0)->f2) && \
|
||||
offsetof(s1, f1) == offsetof(s2, f2))
|
||||
|
||||
#define COMPATFIELD(s1, f1, s2, f2) \
|
||||
(COMPATFIELDAPPROX(s1, f1, s2, f2) && \
|
||||
COMPATLVALUE(((s1 *)0)->f1, ((s2 *)0)->f2))
|
||||
|
||||
|
||||
/* NONNEGATIVE -- test that value is greater than or equal to zero
|
||||
*
|
||||
* We'd like to write "x >= 0" but when x belongs to an unsigned
|
||||
* integral type then this results in a "comparison of unsigned
|
||||
* expression >= 0 is always true" warning from GCC if -Wextra is
|
||||
* specified. We also don't want to remove these assertions because
|
||||
* they protect us against errors if the type of x should ever be
|
||||
* changed to a signed type on some platform.
|
||||
*
|
||||
* Note that this macro evaluates its argument twice.
|
||||
*/
|
||||
#define NONNEGATIVE(x) ((x) == 0 || (x) > 0)
|
||||
|
||||
#endif /* check_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
208
mps/code/clock.h
Normal file
208
mps/code/clock.h
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/* clock.h -- Fast clocks and timers
|
||||
*
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* $Id$
|
||||
*
|
||||
* .design: <design/clock>.
|
||||
*/
|
||||
|
||||
#ifndef clock_h
|
||||
#define clock_h
|
||||
|
||||
#include "mpmtypes.h" /* for Word */
|
||||
|
||||
|
||||
/* EVENT_CLOCK -- fast event timestamp clock
|
||||
*
|
||||
* On platforms that support it, we want to stamp events with a very cheap
|
||||
* and fast high-resolution timer.
|
||||
*/
|
||||
|
||||
/* Microsoft C provides an intrinsic for the Intel rdtsc instruction.
|
||||
* <https://docs.microsoft.com/en-us/cpp/intrinsics/rdtsc>
|
||||
*/
|
||||
#if (defined(MPS_ARCH_I3) || defined(MPS_ARCH_I6)) && defined(MPS_BUILD_MV)
|
||||
|
||||
typedef unsigned __int64 EventClock;
|
||||
|
||||
typedef union EventClockUnion {
|
||||
struct {
|
||||
unsigned low, high;
|
||||
} half;
|
||||
unsigned __int64 whole;
|
||||
} EventClockUnion;
|
||||
|
||||
#define EVENT_CLOCK_MAKE(lvalue, low, high) \
|
||||
BEGIN \
|
||||
((EventClockUnion*)&(lvalue))->half.low = (low); \
|
||||
((EventClockUnion*)&(lvalue))->half.high = (high); \
|
||||
END
|
||||
|
||||
#if _MSC_VER >= 1400
|
||||
|
||||
#pragma intrinsic(__rdtsc)
|
||||
|
||||
#define EVENT_CLOCK(lvalue) \
|
||||
BEGIN \
|
||||
(lvalue) = __rdtsc(); \
|
||||
END
|
||||
|
||||
#else /* _MSC_VER < 1400 */
|
||||
|
||||
/* This is mostly a patch for Open Dylan's bootstrap on Windows, which is
|
||||
using Microsoft Visual Studio 6 because of support for CodeView debugging
|
||||
information. */
|
||||
|
||||
#include "mpswin.h" /* KILL IT WITH FIRE! */
|
||||
|
||||
#define EVENT_CLOCK(lvalue) \
|
||||
BEGIN \
|
||||
LARGE_INTEGER _count; \
|
||||
QueryPerformanceCounter(&_count); \
|
||||
(lvalue) = _count.QuadPart; \
|
||||
END
|
||||
|
||||
#endif /* _MSC_VER < 1400 */
|
||||
|
||||
#if defined(MPS_ARCH_I3)
|
||||
|
||||
/* We can't use a shift to get the top half of the 64-bit event clock,
|
||||
because that introduces a dependency on `__aullshr` in the C run-time. */
|
||||
|
||||
#define EVENT_CLOCK_PRINT(stream, clock) \
|
||||
fprintf(stream, "%08lX%08lX", \
|
||||
(*(EventClockUnion *)&(clock)).half.high, \
|
||||
(*(EventClockUnion *)&(clock)).half.low)
|
||||
|
||||
#define EVENT_CLOCK_WRITE(stream, depth, clock) \
|
||||
WriteF(stream, depth, "$W$W", \
|
||||
(*(EventClockUnion *)&(clock)).half.high, \
|
||||
(*(EventClockUnion *)&(clock)).half.low, \
|
||||
NULL)
|
||||
|
||||
#elif defined(MPS_ARCH_I6)
|
||||
|
||||
#if defined(MPS_BUILD_MV)
|
||||
|
||||
#define EVENT_CLOCK_PRINT(stream, clock) \
|
||||
fprintf(stream, "%016llX", (clock));
|
||||
|
||||
#else
|
||||
|
||||
#define EVENT_CLOCK_PRINT(stream, clock) \
|
||||
fprintf(stream, "%016lX", (clock));
|
||||
|
||||
#endif
|
||||
|
||||
#define EVENT_CLOCK_WRITE(stream, depth, clock) \
|
||||
WriteF(stream, depth, "$W", (WriteFW)(clock), NULL)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* Microsoft C on Intel */
|
||||
|
||||
/* If we have GCC or Clang, assemble the rdtsc instruction */
|
||||
#if !defined(EVENT_CLOCK) && \
|
||||
(defined(MPS_ARCH_I3) || defined(MPS_ARCH_I6)) && \
|
||||
(defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL))
|
||||
|
||||
/* Use __extension__ to enable use of a 64-bit type on 32-bit pedantic
|
||||
GCC or Clang. */
|
||||
__extension__ typedef unsigned long long EventClock;
|
||||
|
||||
#define EVENT_CLOCK_MAKE(lvalue, low, high) \
|
||||
((lvalue) = ((EventClock)(high) << 32) + ((EventClock)(low) & (0xfffffffful)))
|
||||
|
||||
/* Clang provides a cross-platform builtin for a fast timer, but it
|
||||
* was not available on Mac OS X 10.8 until the release of XCode 4.6.
|
||||
* <https://clang.llvm.org/docs/LanguageExtensions.html#builtins>
|
||||
*/
|
||||
#if defined(MPS_BUILD_LL)
|
||||
|
||||
#if __has_builtin(__builtin_readcyclecounter)
|
||||
|
||||
#define EVENT_CLOCK(lvalue) \
|
||||
BEGIN \
|
||||
(lvalue) = __builtin_readcyclecounter(); \
|
||||
END
|
||||
|
||||
#endif /* __has_builtin(__builtin_readcyclecounter) */
|
||||
|
||||
#endif /* Clang */
|
||||
|
||||
#ifndef EVENT_CLOCK
|
||||
|
||||
#define EVENT_CLOCK(lvalue) \
|
||||
BEGIN \
|
||||
unsigned _l, _h; \
|
||||
__asm__ __volatile__("rdtsc" : "=a"(_l), "=d"(_h)); \
|
||||
(lvalue) = ((EventClock)_h << 32) | _l; \
|
||||
END
|
||||
|
||||
#endif
|
||||
|
||||
/* The __extension__ keyword doesn't work on printf formats, so we
|
||||
concatenate two 32-bit hex numbers to print the 64-bit value. */
|
||||
#define EVENT_CLOCK_PRINT(stream, clock) \
|
||||
fprintf(stream, "%08lX%08lX", \
|
||||
(unsigned long)((clock) >> 32), \
|
||||
(unsigned long)((clock) & 0xffffffff))
|
||||
|
||||
#define EVENT_CLOCK_WRITE(stream, depth, clock) \
|
||||
WriteF(stream, depth, "$W$W", (WriteFW)((clock) >> 32), (WriteFW)clock, NULL)
|
||||
|
||||
#endif /* Intel, GCC or Clang */
|
||||
|
||||
/* no fast clock, use plinth, probably from the C library */
|
||||
#ifndef EVENT_CLOCK
|
||||
|
||||
typedef mps_clock_t EventClock;
|
||||
|
||||
#define EVENT_CLOCK_MAKE(lvalue, low, high) \
|
||||
((lvalue) = ((EventClock)(high) << 32) + ((EventClock)(low) & (0xfffffffful)))
|
||||
|
||||
#define EVENT_CLOCK(lvalue) \
|
||||
BEGIN \
|
||||
(lvalue) = mps_clock(); \
|
||||
END
|
||||
|
||||
#define EVENT_CLOCK_PRINT(stream, clock) \
|
||||
fprintf(stream, "%lu", (unsigned long)clock)
|
||||
|
||||
#define EVENT_CLOCK_WRITE(stream, depth, clock) \
|
||||
WriteF(stream, depth, "$W", (WriteFW)clock, NULL)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* clock_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
749
mps/code/comm.gmk
Normal file
749
mps/code/comm.gmk
Normal file
|
|
@ -0,0 +1,749 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# comm.gmk: COMMON GNUMAKEFILE FRAGMENT
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This makefile fragment is included in more specific makefiles for
|
||||
# platforms which use GNU make.
|
||||
#
|
||||
# PARAMETERS
|
||||
#
|
||||
# Assumes the following variables and definitions:
|
||||
# EXTRA_TARGETS a list of extra targets to build
|
||||
# CFLAGSCOMPILER a list of flags for all compilations
|
||||
# CFLAGSCOMPILERSTRICT a list of flags for almost all compilations
|
||||
# CFLAGSCOMPILERLAX a list of flags for compilations which can't be as
|
||||
# strict (e.g. because they have to include a third-
|
||||
# party header file that isn't -ansi -pedantic).
|
||||
# CFLAGSDEBUG a list of flags for compilations with maximum debug
|
||||
# information, and any optimization possible
|
||||
# CFLAGSOPT a list of flags for compilations with maximum
|
||||
# optimization, and any debug info possible
|
||||
# CC the command for the C compiler
|
||||
# LINKFLAGS a list of flags passed to the linker
|
||||
# ARFLAGSPFM platform-specific flags for ar
|
||||
# RANLIB the command to index a library (default: none needed)
|
||||
# gendep optionally defined to be a command sequence for
|
||||
# generating the dependency file (.d) from a C file (.c);
|
||||
# it is used in a rule of the form:
|
||||
# $(PFM)/$(VARIETY)/%.d: %.c
|
||||
# PFM platform code, e.g. "sus8gc"
|
||||
# LIBS extra libraries to include in tests (usually "-lm")
|
||||
# NOISY if defined and non-empty, causes commands to be emitted
|
||||
# MPMPF platform-dependent C sources for the "mpm" part
|
||||
# MPMS assembler sources for the "mpm" part (.s files)
|
||||
#
|
||||
# %%PART: When adding a new part, add a new parameter above for the
|
||||
# files included in the part.
|
||||
#
|
||||
# EDITING
|
||||
#
|
||||
# To add new targets, varieties, and parts:
|
||||
# Search for the string "%%TARGET", "%%VARIETY", or "%%PART" in this
|
||||
# makefile and follow the instructions. If you're adding a part, you'll
|
||||
# have to change the makefiles for all the platforms which use this
|
||||
# makefile to define the source list for that part, and the GNUmakefile
|
||||
# to include a recursive call to the name of that part.
|
||||
#
|
||||
# CHECK PARAMETERS
|
||||
#
|
||||
# Old versions of GNU make don't have the $(error) function, but lines
|
||||
# starting "error" cause it to exit with an error. [These only test to
|
||||
# see whether the symbol is defined. We could be more thorough and
|
||||
# test the syntax and content. -- richard 1995-09-07]
|
||||
|
||||
ifndef CC
|
||||
error "comm.gmk: CC not defined"
|
||||
endif
|
||||
ifndef CFLAGSCOMPILER
|
||||
error "comm.gmk: CFLAGSCOMPILER not defined"
|
||||
endif
|
||||
ifndef CFLAGSDEBUG
|
||||
error "comm.gmk: CFLAGSDEBUG not defined"
|
||||
endif
|
||||
ifndef CFLAGSOPT
|
||||
error "comm.gmk: CFLAGSOPT not defined"
|
||||
endif
|
||||
|
||||
|
||||
# TELEMETRY TARGETS
|
||||
|
||||
EVENT_TARGETS = mpseventcnv mpseventpy mpseventsql mpseventtxt
|
||||
|
||||
|
||||
# EXTRA TARGETS
|
||||
#
|
||||
# Don't build mpseventsql by default (might not have sqlite3 installed),
|
||||
# but do build the other event target.
|
||||
|
||||
EXTRA_TARGETS ?= $(filter-out mpseventsql,$(EVENT_TARGETS))
|
||||
|
||||
|
||||
#
|
||||
# %%PART: When adding a new part, add checks for the parameter with the
|
||||
# sources for the new part.
|
||||
|
||||
ifndef PFM
|
||||
error "comm.gmk: PFM not defined"
|
||||
endif
|
||||
ifndef MPMPF
|
||||
error "comm.gmk: MPMPF not defined"
|
||||
endif
|
||||
|
||||
|
||||
# DECLARATIONS
|
||||
|
||||
ifdef NOISY
|
||||
ECHO = :
|
||||
else
|
||||
.SILENT:
|
||||
ECHO = echo
|
||||
endif
|
||||
|
||||
.PHONY: phony
|
||||
|
||||
|
||||
# C FLAGS
|
||||
|
||||
# These flags are included in all compilations.
|
||||
# Avoid using PFMDEFS in platform makefiles, as they prevent the MPS being
|
||||
# built with a simple command like "cc -c mps.c".
|
||||
CFLAGSCOMMONSTRICT = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERSTRICT)
|
||||
CFLAGSCOMMONLAX = $(PFMDEFS) $(CFLAGSCOMPILER) $(CFLAGSCOMPILERLAX)
|
||||
|
||||
# %%VARIETY: When adding a new variety, define a macro containing the set
|
||||
# of flags for the new variety.
|
||||
|
||||
# These flags are added to compilations for the indicated variety.
|
||||
CFRASH = -DCONFIG_VAR_RASH $(CFLAGSOPT)
|
||||
CFHOT = -DCONFIG_VAR_HOT $(CFLAGSOPT)
|
||||
CFCOOL = -DCONFIG_VAR_COOL $(CFLAGSDEBUG)
|
||||
|
||||
# Bind CFLAGSVARIETY to the appropriate set of flags for the variety.
|
||||
# %%VARIETY: When adding a new variety, add a test for the variety and
|
||||
# set CFLAGSVARIETY here.
|
||||
ifeq ($(VARIETY),rash)
|
||||
CFLAGSVARIETY=$(CFRASH)
|
||||
else
|
||||
ifeq ($(VARIETY),hot)
|
||||
CFLAGSVARIETY=$(CFHOT)
|
||||
else
|
||||
ifeq ($(VARIETY),cool)
|
||||
CFLAGSVARIETY=$(CFCOOL)
|
||||
else
|
||||
ifneq ($(VARIETY),)
|
||||
$(error Variety "$(VARIETY)" not recognized: must be rash/hot/cool)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGSSTRICT=$(CFLAGSCOMMONSTRICT) $(CFLAGSVARIETY) $(CFLAGS)
|
||||
CFLAGSLAX=$(CFLAGSCOMMONLAX) $(CFLAGSVARIETY) $(CFLAGS)
|
||||
ARFLAGS=rc$(ARFLAGSPFM)
|
||||
|
||||
|
||||
# == Common definitions ==
|
||||
# %%PART: When adding a new part, add it here, unless it's platform-specific
|
||||
# These values are defined here because they have no variation between
|
||||
# platforms.
|
||||
|
||||
AMC = poolamc.c
|
||||
AMS = poolams.c
|
||||
AWL = poolawl.c
|
||||
LO = poollo.c
|
||||
SNC = poolsnc.c
|
||||
POOLN = pooln.c
|
||||
MV2 = poolmv2.c
|
||||
MVFF = poolmvff.c
|
||||
TESTLIB = testlib.c
|
||||
TESTTHR = testthrix.c
|
||||
FMTDY = fmtdy.c fmtno.c
|
||||
FMTDYTST = fmtdy.c fmtno.c fmtdytst.c
|
||||
FMTHETST = fmthe.c fmtdy.c fmtno.c fmtdytst.c
|
||||
FMTSCM = fmtscheme.c
|
||||
PLINTH = mpsliban.c mpsioan.c
|
||||
MPMCOMMON = \
|
||||
abq.c \
|
||||
arena.c \
|
||||
arenacl.c \
|
||||
arenavm.c \
|
||||
arg.c \
|
||||
boot.c \
|
||||
bt.c \
|
||||
buffer.c \
|
||||
cbs.c \
|
||||
dbgpool.c \
|
||||
dbgpooli.c \
|
||||
event.c \
|
||||
failover.c \
|
||||
format.c \
|
||||
freelist.c \
|
||||
global.c \
|
||||
land.c \
|
||||
ld.c \
|
||||
locus.c \
|
||||
message.c \
|
||||
meter.c \
|
||||
mpm.c \
|
||||
mpsi.c \
|
||||
nailboard.c \
|
||||
policy.c \
|
||||
pool.c \
|
||||
poolabs.c \
|
||||
poolmfs.c \
|
||||
poolmrg.c \
|
||||
protocol.c \
|
||||
range.c \
|
||||
rangetree.c \
|
||||
ref.c \
|
||||
ring.c \
|
||||
root.c \
|
||||
sa.c \
|
||||
sac.c \
|
||||
scan.c \
|
||||
seg.c \
|
||||
shield.c \
|
||||
splay.c \
|
||||
ss.c \
|
||||
table.c \
|
||||
trace.c \
|
||||
traceanc.c \
|
||||
tract.c \
|
||||
trans.c \
|
||||
tree.c \
|
||||
version.c \
|
||||
vm.c \
|
||||
walk.c
|
||||
POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC)
|
||||
MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH)
|
||||
|
||||
|
||||
# These map the source file lists onto object files and dependency files
|
||||
# in the platform/variety directory.
|
||||
#
|
||||
# %%PART: When adding a new part, add a new macro which expands to the files
|
||||
# included in the part.
|
||||
|
||||
ifdef VARIETY
|
||||
MPMOBJ = $(MPM:%.c=$(PFM)/$(VARIETY)/%.o) \
|
||||
$(MPMS:%.s=$(PFM)/$(VARIETY)/%.o)
|
||||
FMTDYOBJ = $(FMTDY:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
FMTDYTSTOBJ = $(FMTDYTST:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
FMTHETSTOBJ = $(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
FMTSCMOBJ = $(FMTSCM:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
PLINTHOBJ = $(PLINTH:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
POOLNOBJ = $(POOLN:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
TESTLIBOBJ = $(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
TESTTHROBJ = $(TESTTHR:%.c=$(PFM)/$(VARIETY)/%.o)
|
||||
endif
|
||||
|
||||
|
||||
# == Test cases ==
|
||||
#
|
||||
# %%TARGET: When adding a new target, add it to one of the variables
|
||||
# in this section. Library components go in LIB_TARGETS.
|
||||
|
||||
LIB_TARGETS=mps.a mpsplan.a
|
||||
|
||||
# Test executables go in TEST_TARGETS.
|
||||
|
||||
TEST_TARGETS=\
|
||||
abqtest \
|
||||
addrobj \
|
||||
airtest \
|
||||
amcss \
|
||||
amcsshe \
|
||||
amcssth \
|
||||
amsss \
|
||||
amssshe \
|
||||
apss \
|
||||
arenacv \
|
||||
awlut \
|
||||
awluthe \
|
||||
awlutth \
|
||||
btcv \
|
||||
bttest \
|
||||
djbench \
|
||||
extcon \
|
||||
finalcv \
|
||||
finaltest \
|
||||
forktest \
|
||||
fotest \
|
||||
gcbench \
|
||||
landtest \
|
||||
locbwcss \
|
||||
lockcov \
|
||||
lockut \
|
||||
locusss \
|
||||
locv \
|
||||
messtest \
|
||||
mpmss \
|
||||
mpsicv \
|
||||
mv2test \
|
||||
nailboardtest \
|
||||
poolncv \
|
||||
qs \
|
||||
sacss \
|
||||
segsmss \
|
||||
sncss \
|
||||
steptest \
|
||||
tagtest \
|
||||
teletest \
|
||||
walkt0 \
|
||||
zcoll \
|
||||
zmess \
|
||||
ztfm
|
||||
|
||||
# This target records programs that we were once able to build but
|
||||
# can't at the moment:
|
||||
|
||||
UNBUILDABLE_TARGETS=\
|
||||
replay # depends on the EPVM pool
|
||||
|
||||
ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS)
|
||||
|
||||
|
||||
# == Pseudo-targets ==
|
||||
|
||||
all: $(ALL_TARGETS)
|
||||
|
||||
|
||||
# == Automated test suites ==
|
||||
#
|
||||
# See design.mps.tests.target.
|
||||
|
||||
TEST_SUITES=testrun testci testall testansi testpollnone
|
||||
|
||||
$(addprefix $(PFM)/$(VARIETY)/,$(TEST_SUITES)): $(TEST_TARGETS)
|
||||
../tool/testrun.sh -s "$(notdir $@)" "$(PFM)/$(VARIETY)"
|
||||
|
||||
|
||||
# == Automated performance testing ==
|
||||
#
|
||||
# See design.mps.tests.target.testratio.
|
||||
|
||||
TESTRATIO_SEED = 1564912146
|
||||
|
||||
define ratio
|
||||
TIME_HOT=$$(/usr/bin/time -p $(PFM)/hot/$(1) -x $(TESTRATIO_SEED) $(2) 2>&1 | tail -2 | awk '{T += $$2} END {print T}'); \
|
||||
TIME_RASH=$$(/usr/bin/time -p $(PFM)/rash/$(1) -x $(TESTRATIO_SEED) $(2) 2>&1 | tail -2 | awk '{T += $$2} END {print T}'); \
|
||||
RATIO=$$(awk "BEGIN{print int(100 * $$TIME_HOT / $$TIME_RASH)}"); \
|
||||
printf "Performance ratio (hot/rash) for $(2): %d%%\n" $$RATIO
|
||||
endef
|
||||
|
||||
testratio: phony
|
||||
$(MAKE) -f $(PFM).gmk VARIETY=hot djbench gcbench
|
||||
$(MAKE) -f $(PFM).gmk VARIETY=rash djbench gcbench
|
||||
$(call ratio,gcbench,amc)
|
||||
$(call ratio,djbench,mvff)
|
||||
|
||||
|
||||
# == MMQA test suite ==
|
||||
#
|
||||
# See test/README for documentation on running the MMQA test suite.
|
||||
|
||||
MMQA=perl test/qa -p $(PFM) -v $(VARIETY)
|
||||
|
||||
$(PFM)/$(VARIETY)/testmmqa:
|
||||
if [ "$(VARIETY)" = "cool" ]; then (cd ../test && $(MMQA) runset testsets/coolonly); fi
|
||||
(cd ../test && $(MMQA) runset testsets/argerr testsets/conerr testsets/passing)
|
||||
|
||||
|
||||
# == Toy Scheme interpreter ==
|
||||
|
||||
testscheme: phony
|
||||
$(MAKE) -C ../example/scheme test
|
||||
|
||||
|
||||
# These convenience targets allow one to type "make foo" to build target
|
||||
# foo in selected varieties (or none, for the latter rule).
|
||||
|
||||
$(LIB_TARGETS) $(TEST_TARGETS) $(EVENT_TARGETS) $(TEST_SUITES) testmmqa: phony
|
||||
ifdef VARIETY
|
||||
$(MAKE) -f $(PFM).gmk TARGET=$@ variety
|
||||
else
|
||||
$(MAKE) -f $(PFM).gmk TARGET=$@ target
|
||||
endif
|
||||
|
||||
|
||||
# "clean" removes the directory containing the build results for the
|
||||
# platform.
|
||||
|
||||
clean: phony
|
||||
$(ECHO) "$(PFM): $@"
|
||||
rm -rf "$(PFM)"
|
||||
|
||||
# "target" builds some varieties of the target named in the TARGET
|
||||
# macro.
|
||||
#
|
||||
# %%VARIETY: When adding a new target, optionally add a recursive make call
|
||||
# for the new variety, if it should be built by default. It probably
|
||||
# shouldn't without a product design decision and an update of the readme
|
||||
# and build manual!
|
||||
#
|
||||
# Note that we build VARIETY=cool before VARIETY=hot because
|
||||
# the former doesn't need to optimize and so detects errors more
|
||||
# quickly; and because the former uses file-at-a-time compilation and
|
||||
# so can pick up where it left off instead of having to start from the
|
||||
# beginning of mps.c
|
||||
|
||||
ifdef TARGET
|
||||
ifndef VARIETY
|
||||
target: phony
|
||||
$(MAKE) -f $(PFM).gmk VARIETY=cool variety
|
||||
$(MAKE) -f $(PFM).gmk VARIETY=hot variety
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# "variety" builds the target named in the TARGET macro using the
|
||||
# variety named in the VARIETY macro.
|
||||
|
||||
ifdef VARIETY
|
||||
ifdef TARGET
|
||||
variety: $(PFM)/$(VARIETY)/$(TARGET)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# THE MPS LIBRARY
|
||||
#
|
||||
# The MPS library is built in two ways:
|
||||
#
|
||||
# 1. In the usual way, from a pile of object files compiled from their
|
||||
# corresponding sources.
|
||||
#
|
||||
# 2. From mps.c, which effectively concatenates all the sources, allowing
|
||||
# important global optimisation and inlining to occur.
|
||||
#
|
||||
# We mostly use the method (2), because it is fast to compile and execute.
|
||||
# But we use method (1) for some varieties to ensure correctness of
|
||||
# code (linkage errors are masked by (2)) and to maintain a correct list
|
||||
# of source files in case method (2) won't work on some future constrained
|
||||
# platform.
|
||||
#
|
||||
# %%VARIETY: When adding a new variety, add a rule for how to build the
|
||||
# MPS library for the variety.
|
||||
|
||||
$(PFM)/rash/mps.a: $(PFM)/rash/mps.o
|
||||
$(PFM)/hot/mps.a: $(PFM)/hot/mps.o
|
||||
$(PFM)/cool/mps.a: $(MPMOBJ)
|
||||
|
||||
|
||||
# OTHER GENUINE TARGETS
|
||||
#
|
||||
# Each line defines an executable or library target to be built and the
|
||||
# object files it is built from. These lines add dependencies to the
|
||||
# generic rules below, and should not include commands to execute.
|
||||
#
|
||||
# %%TARGET: When adding a new target, add the dependencies for the new target
|
||||
# here.
|
||||
|
||||
ifdef VARIETY
|
||||
|
||||
$(PFM)/$(VARIETY)/abqtest: $(PFM)/$(VARIETY)/abqtest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/addrobj: $(PFM)/$(VARIETY)/addrobj.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/airtest: $(PFM)/$(VARIETY)/airtest.o \
|
||||
$(FMTSCMOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/amcss: $(PFM)/$(VARIETY)/amcss.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/amcsshe: $(PFM)/$(VARIETY)/amcsshe.o \
|
||||
$(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/amcssth: $(PFM)/$(VARIETY)/amcssth.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/amsss: $(PFM)/$(VARIETY)/amsss.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/amssshe: $(PFM)/$(VARIETY)/amssshe.o \
|
||||
$(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/apss: $(PFM)/$(VARIETY)/apss.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/arenacv: $(PFM)/$(VARIETY)/arenacv.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/awlut: $(PFM)/$(VARIETY)/awlut.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/awluthe: $(PFM)/$(VARIETY)/awluthe.o \
|
||||
$(FMTHETSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/awlutth: $(PFM)/$(VARIETY)/awlutth.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/btcv: $(PFM)/$(VARIETY)/btcv.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/bttest: $(PFM)/$(VARIETY)/bttest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/djbench: $(PFM)/$(VARIETY)/djbench.o \
|
||||
$(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)/$(VARIETY)/extcon: $(PFM)/$(VARIETY)/extcon.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/finaltest: $(PFM)/$(VARIETY)/finaltest.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/forktest: $(PFM)/$(VARIETY)/forktest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/gcbench: $(PFM)/$(VARIETY)/gcbench.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)/$(VARIETY)/landtest: $(PFM)/$(VARIETY)/landtest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/locbwcss: $(PFM)/$(VARIETY)/locbwcss.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/lockcov: $(PFM)/$(VARIETY)/lockcov.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/lockut: $(PFM)/$(VARIETY)/lockut.o \
|
||||
$(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/locusss: $(PFM)/$(VARIETY)/locusss.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/locv: $(PFM)/$(VARIETY)/locv.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/messtest: $(PFM)/$(VARIETY)/messtest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mpmss: $(PFM)/$(VARIETY)/mpmss.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mpsicv: $(PFM)/$(VARIETY)/mpsicv.o \
|
||||
$(FMTDYTSTOBJ) $(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mv2test: $(PFM)/$(VARIETY)/mv2test.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/nailboardtest: $(PFM)/$(VARIETY)/nailboardtest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/poolncv: $(PFM)/$(VARIETY)/poolncv.o \
|
||||
$(POOLNOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/qs: $(PFM)/$(VARIETY)/qs.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/sacss: $(PFM)/$(VARIETY)/sacss.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/segsmss: $(PFM)/$(VARIETY)/segsmss.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/sncss: $(PFM)/$(VARIETY)/sncss.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/steptest: $(PFM)/$(VARIETY)/steptest.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/tagtest: $(PFM)/$(VARIETY)/tagtest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/teletest: $(PFM)/$(VARIETY)/teletest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/walkt0: $(PFM)/$(VARIETY)/walkt0.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/zcoll: $(PFM)/$(VARIETY)/zcoll.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/zmess: $(PFM)/$(VARIETY)/zmess.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/ztfm: $(PFM)/$(VARIETY)/ztfm.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mpseventcnv: $(PFM)/$(VARIETY)/eventcnv.o \
|
||||
$(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mpseventpy: $(PFM)/$(VARIETY)/eventpy.o \
|
||||
$(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mpseventtxt: $(PFM)/$(VARIETY)/eventtxt.o \
|
||||
$(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mpseventsql: $(PFM)/$(VARIETY)/eventsql.o \
|
||||
$(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/replay: $(PFM)/$(VARIETY)/replay.o \
|
||||
$(PFM)/$(VARIETY)/eventrep.o \
|
||||
$(PFM)/$(VARIETY)/table.o \
|
||||
$(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/mpsplan.a: $(PLINTHOBJ)
|
||||
|
||||
endif
|
||||
|
||||
|
||||
# GENERIC RULES
|
||||
#
|
||||
# These generate build output in the <pfm>/<variety> directory.
|
||||
|
||||
# Object files
|
||||
|
||||
define run-cc-strict
|
||||
$(ECHO) "$(PFM): $@"
|
||||
mkdir -p $(PFM)
|
||||
mkdir -p $(PFM)/$(VARIETY)
|
||||
$(CC) $(CFLAGSSTRICT) -c -o $@ $<
|
||||
endef
|
||||
|
||||
define run-cc-lax
|
||||
$(ECHO) "$(PFM): $@ - compiling with lax flags."
|
||||
mkdir -p $(PFM)
|
||||
mkdir -p $(PFM)/$(VARIETY)
|
||||
$(CC) $(CFLAGSLAX) -c -o $@ $<
|
||||
endef
|
||||
|
||||
# .rule.c-to-o:
|
||||
$(PFM)/$(VARIETY)/%.o: %.c
|
||||
$(run-cc-strict)
|
||||
|
||||
$(PFM)/$(VARIETY)/eventsql.o: eventsql.c
|
||||
$(run-cc-lax)
|
||||
|
||||
$(PFM)/$(VARIETY)/%.o: %.s
|
||||
$(run-cc-strict)
|
||||
|
||||
$(PFM)/$(VARIETY)/%.o: %.S
|
||||
$(run-cc-strict)
|
||||
|
||||
# Dependencies
|
||||
#
|
||||
# These are included into _this_ makefile (see below). GNU make does the
|
||||
# right thing as long as it knows how to make the dependency files before
|
||||
# including them.
|
||||
|
||||
ifdef gendep
|
||||
|
||||
$(PFM)/$(VARIETY)/%.d: %.c
|
||||
$(ECHO) "$(PFM): $@"
|
||||
mkdir -p $(PFM)
|
||||
mkdir -p $(PFM)/$(VARIETY)
|
||||
$(gendep)
|
||||
|
||||
ifdef VARIETY
|
||||
ifdef TARGET
|
||||
|
||||
# %%VARIETY: When adding a new variety, add the dependencies files for it
|
||||
# here.
|
||||
ifeq ($(VARIETY),rash)
|
||||
include $(PFM)/$(VARIETY)/mps.d
|
||||
else
|
||||
ifeq ($(VARIETY),hot)
|
||||
include $(PFM)/$(VARIETY)/mps.d
|
||||
else
|
||||
include $(MPM:%.c=$(PFM)/$(VARIETY)/%.d)
|
||||
endif # VARIETY != hot
|
||||
endif # VARIETY != rash
|
||||
|
||||
# %%PART: When adding a new part, add the dependencies file for the
|
||||
# new part here.
|
||||
include \
|
||||
$(FMTDY:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(FMTDYTST:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(FMTHETST:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(FMTSCM:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(PLINTH:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(POOLN:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(TESTLIB:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(TESTTHR:%.c=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(EXTRA_TARGETS:mps%=$(PFM)/$(VARIETY)/%.d) \
|
||||
$(TEST_TARGETS:%=$(PFM)/$(VARIETY)/%.d)
|
||||
|
||||
endif # !defined TARGET
|
||||
endif # !defined VARIETY
|
||||
|
||||
endif # !defined gendep
|
||||
|
||||
# Library
|
||||
|
||||
ifndef RANLIB
|
||||
RANLIB = :
|
||||
endif
|
||||
|
||||
$(PFM)/$(VARIETY)/%.a:
|
||||
$(ECHO) "$(PFM): $@"
|
||||
rm -f $@
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
$(RANLIB) $@
|
||||
|
||||
# Executable
|
||||
|
||||
$(PFM)/$(VARIETY)/%:
|
||||
$(ECHO) "$(PFM): $@"
|
||||
$(CC) $(CFLAGSSTRICT) $(LINKFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
$(PFM)/$(VARIETY)/mpseventsql:
|
||||
$(ECHO) "$(PFM): $@"
|
||||
$(CC) $(CFLAGSLAX) $(LINKFLAGS) -o $@ $^ $(LIBS) -lsqlite3
|
||||
|
||||
# Special targets for development
|
||||
|
||||
# Currently FreeBSD 7 GCC 4.2.1 is the best platform we have for warning
|
||||
# us about strict aliasing rule violations caused by type puns. This
|
||||
# target reveals them, and produces an assembler output file that can be
|
||||
# examined to see if they're actually dangerous. RB 2012-09-07
|
||||
|
||||
find-puns: phony
|
||||
{ echo '#include "mps.c"'; echo '#include "fmtdy.c"'; } | \
|
||||
gcc -S -fverbose-asm -ansi -pedantic -Wall -Wstrict-aliasing=2 -O3 -x c -o pun.s -
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
416
mps/code/commpost.nmk
Normal file
416
mps/code/commpost.nmk
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
# commpost.nmk: SECOND COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*-
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Second common makefile fragment for w3*mv.nmk. See commpre.nmk
|
||||
|
||||
|
||||
# == Pseudo-targets ==
|
||||
|
||||
all: $(ALL_TARGETS)
|
||||
|
||||
|
||||
# Convenience targets
|
||||
|
||||
$(ALL_TARGETS) $(OPTIONAL_TARGETS):
|
||||
!IFDEF VARIETY
|
||||
$(MAKE) /nologo /f $(PFM).nmk TARGET=$@ variety
|
||||
!ELSE
|
||||
$(MAKE) /nologo /f $(PFM).nmk TARGET=$@ target
|
||||
!ENDIF
|
||||
|
||||
# "clean" removes the directory containing the build results.
|
||||
# Depends on there being no file called "clean".
|
||||
# Note that we suspend error processing on the if line because rmdir
|
||||
# sometimes exits with an error and the message "The directory is not
|
||||
# empty" even if the /s option is given. See job003854.
|
||||
|
||||
clean:
|
||||
$(ECHO) $(PFM): $@
|
||||
-if exist $(PFM) rmdir /q /s $(PFM)
|
||||
|
||||
# target target
|
||||
# %%VARIETY: When adding a new variety, optionally, add a recursive make
|
||||
# call for the new variety, if it should be built by default. It probably
|
||||
# shouldn't without a product design decision and an update of the readme
|
||||
# and build manual!
|
||||
# Depends on there being no file called "target".
|
||||
|
||||
!IFDEF TARGET
|
||||
!IFNDEF VARIETY
|
||||
target:
|
||||
$(MAKE) /nologo /f $(PFM).nmk VARIETY=cool variety
|
||||
$(MAKE) /nologo /f $(PFM).nmk VARIETY=hot variety
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# variety
|
||||
# Depends on there being no file called "variety".
|
||||
|
||||
!IFDEF VARIETY
|
||||
!IFDEF TARGET
|
||||
variety: $(PFM)\$(VARIETY)\$(TARGET)
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
# testrun testci testall testansi testpollnone
|
||||
# Runs automated test cases. See design.mps.tests.target.
|
||||
|
||||
testrun testci testall testansi testpollnone: $(TEST_TARGETS)
|
||||
!IFDEF VARIETY
|
||||
..\tool\testrun.bat $(PFM) $(VARIETY) $@
|
||||
!ELSE
|
||||
$(MAKE) /nologo /f $(PFM).nmk VARIETY=cool $@
|
||||
$(MAKE) /nologo /f $(PFM).nmk VARIETY=hot $@
|
||||
!ENDIF
|
||||
|
||||
|
||||
# FLAGS AMALGAMATION
|
||||
#
|
||||
# %%VARIETY: When adding a new variety, add the following macros that
|
||||
# expand to sets of flags that the variety should use:
|
||||
#
|
||||
# CFLAGS -- when compiling C;
|
||||
# CFLAGSSQL -- when compiling mpseventsql;
|
||||
# LINKFLAGS -- when building executables;
|
||||
# LIBFLAGS -- when building libraries.
|
||||
|
||||
!IF "$(VARIETY)" == "hot"
|
||||
CFLAGS=$(CFLAGSCOMMONPRE) $(CFHOT) $(CFLAGSCOMMONPOST)
|
||||
CFLAGSSQL=$(CFLAGSSQLPRE) $(CFHOT) $(CFLAGSSQLPOST)
|
||||
LINKFLAGS=$(LINKFLAGSCOMMON) $(LFHOT)
|
||||
LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSHOT)
|
||||
|
||||
!ELSEIF "$(VARIETY)" == "cool"
|
||||
CFLAGS=$(CFLAGSCOMMONPRE) $(CFCOOL) $(CFLAGSCOMMONPOST)
|
||||
CFLAGSSQL=$(CFLAGSSQLPRE) $(CFCOOL) $(CFLAGSSQLPOST)
|
||||
LINKFLAGS=$(LINKFLAGSCOMMON) $(LFCOOL)
|
||||
LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSCOOL)
|
||||
|
||||
!ELSEIF "$(VARIETY)" == "rash"
|
||||
CFLAGS=$(CFLAGSCOMMONPRE) $(CFRASH) $(CFLAGSCOMMONPOST)
|
||||
CFLAGSSQL=$(CFLAGSSQLPRE) $(CFRASH) $(CFLAGSSQLPOST)
|
||||
LINKFLAGS=$(LINKFLAGSCOMMON) $(LFRASH)
|
||||
LIBFLAGS=$(LIBFLAGSCOMMON) $(LIBFLAGSRASH)
|
||||
|
||||
!ENDIF
|
||||
|
||||
|
||||
# SOURCE TO OBJECT FILE MAPPINGS
|
||||
#
|
||||
# %%PART: When adding a new part, add new macros which expand to the object
|
||||
# files included in the part
|
||||
#
|
||||
# Note: nmake doesn't expand variables within a string replacement
|
||||
# operation. We work around this by writing out a temporary makefile
|
||||
# and including it.
|
||||
|
||||
TEMPMAKE=$(TEMP)\mps.nmk
|
||||
!IF [echo MPMOBJ0 = $$(MPM:[=$(PFM)\$(VARIETY)\) > $(TEMPMAKE)] == 0 \
|
||||
&& [echo FMTDYOBJ0 = $$(FMTDY:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
|
||||
&& [echo FMTTESTOBJ0 = $$(FMTTEST:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
|
||||
&& [echo FMTSCHEMEOBJ0 = $$(FMTSCHEME:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
|
||||
&& [echo POOLNOBJ0 = $$(POOLN:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
|
||||
&& [echo TESTLIBOBJ0 = $$(TESTLIB:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0 \
|
||||
&& [echo TESTTHROBJ0 = $$(TESTTHR:[=$(PFM)\$(VARIETY)\) >> $(TEMPMAKE)] == 0
|
||||
!INCLUDE $(TEMPMAKE)
|
||||
!IF [del $(TEMPMAKE)] != 0
|
||||
!ERROR Failed to delete $(TEMPMAKE)
|
||||
!ENDIF
|
||||
!ENDIF
|
||||
|
||||
MPMOBJ = $(MPMOBJ0:]=.obj)
|
||||
FMTDYOBJ = $(FMTDYOBJ0:]=.obj)
|
||||
FMTTESTOBJ = $(FMTTESTOBJ0:]=.obj)
|
||||
FMTSCHEMEOBJ = $(FMTSCHEMEOBJ0:]=.obj)
|
||||
POOLNOBJ = $(POOLNOBJ0:]=.obj)
|
||||
TESTLIBOBJ = $(TESTLIBOBJ0:]=.obj)
|
||||
TESTTHROBJ = $(TESTTHROBJ0:]=.obj)
|
||||
|
||||
|
||||
# THE MPS LIBRARY
|
||||
#
|
||||
# The MPS library is built in two ways:
|
||||
#
|
||||
# 1. In the usual way, from a pile of object files compiled from their
|
||||
# corresponding sources.
|
||||
#
|
||||
# 2. From mps.c, which effectively concatenates all the sources, allowing
|
||||
# important global optimisation and inlining to occur.
|
||||
#
|
||||
# We mostly use the method (2), because it is fast to compile and execute.
|
||||
# But we use method (1) for some varieties to ensure correctness of
|
||||
# code (linkage errors are masked by (2)) and to maintain a correct list
|
||||
# of source files in case method (1) won't work on some future constrained
|
||||
# platform.
|
||||
#
|
||||
# %%VARIETY: When adding a new variety, add a rule for how to build the
|
||||
# MPS library for the variety
|
||||
|
||||
$(PFM)\rash\mps.lib: $(PFM)\rash\mps.obj
|
||||
$(ECHO) $@
|
||||
$(LIBMAN) $(LIBFLAGS) /OUT:$@ $**
|
||||
|
||||
$(PFM)\hot\mps.lib: $(PFM)\hot\mps.obj
|
||||
$(ECHO) $@
|
||||
$(LIBMAN) $(LIBFLAGS) /OUT:$@ $**
|
||||
|
||||
$(PFM)\cool\mps.lib: $(MPMOBJ)
|
||||
$(ECHO) $@
|
||||
$(LIBMAN) $(LIBFLAGS) /OUT:$@ $**
|
||||
|
||||
|
||||
# OTHER GENUINE TARGETS
|
||||
#
|
||||
# Each line defines an executable or library target to be built and the object
|
||||
# files it is build from. For an executable these lines add dependencies to
|
||||
# the generic rules below, and should not include commands to execute.
|
||||
# For a library this is not possible and the target should include commands
|
||||
# to build it.
|
||||
# %%TARGET: When adding a new target, add your new target here
|
||||
|
||||
!IFDEF VARIETY
|
||||
|
||||
$(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\addrobj.exe: $(PFM)\$(VARIETY)\addrobj.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\airtest.exe: $(PFM)\$(VARIETY)\airtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTSCHEMEOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\amcss.exe: $(PFM)\$(VARIETY)\amcss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\amcsshe.exe: $(PFM)\$(VARIETY)\amcsshe.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\amcssth.exe: $(PFM)\$(VARIETY)\amcssth.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\amsss.exe: $(PFM)\$(VARIETY)\amsss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\amssshe.exe: $(PFM)\$(VARIETY)\amssshe.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\apss.exe: $(PFM)\$(VARIETY)\apss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\arenacv.exe: $(PFM)\$(VARIETY)\arenacv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\awlut.exe: $(PFM)\$(VARIETY)\awlut.obj \
|
||||
$(FMTTESTOBJ) \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\awluthe.exe: $(PFM)\$(VARIETY)\awluthe.obj \
|
||||
$(FMTTESTOBJ) \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\awlutth.exe: $(PFM)\$(VARIETY)\awlutth.obj \
|
||||
$(FMTTESTOBJ) \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\btcv.exe: $(PFM)\$(VARIETY)\btcv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\bttest.exe: $(PFM)\$(VARIETY)\bttest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \
|
||||
$(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\extcon.exe: $(PFM)\$(VARIETY)\extcon.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\finaltest.exe: $(PFM)\$(VARIETY)\finaltest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\fotest.exe: $(PFM)\$(VARIETY)\fotest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\gcbench.exe: $(PFM)\$(VARIETY)\gcbench.obj \
|
||||
$(FMTTESTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\landtest.exe: $(PFM)\$(VARIETY)\landtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\locbwcss.exe: $(PFM)\$(VARIETY)\locbwcss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\lockcov.exe: $(PFM)\$(VARIETY)\lockcov.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\lockut.exe: $(PFM)\$(VARIETY)\lockut.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\locusss.exe: $(PFM)\$(VARIETY)\locusss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\locv.exe: $(PFM)\$(VARIETY)\locv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\messtest.exe: $(PFM)\$(VARIETY)\messtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\mpmss.exe: $(PFM)\$(VARIETY)\mpmss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\mpsicv.exe: $(PFM)\$(VARIETY)\mpsicv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\mv2test.exe: $(PFM)\$(VARIETY)\mv2test.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\nailboardtest.exe: $(PFM)\$(VARIETY)\nailboardtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\poolncv.exe: $(PFM)\$(VARIETY)\poolncv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(POOLNOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\qs.exe: $(PFM)\$(VARIETY)\qs.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\sacss.exe: $(PFM)\$(VARIETY)\sacss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\segsmss.exe: $(PFM)\$(VARIETY)\segsmss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\sncss.exe: $(PFM)\$(VARIETY)\sncss.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\steptest.exe: $(PFM)\$(VARIETY)\steptest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\tagtest.exe: $(PFM)\$(VARIETY)\tagtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\teletest.exe: $(PFM)\$(VARIETY)\teletest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\walkt0.exe: $(PFM)\$(VARIETY)\walkt0.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\zcoll.exe: $(PFM)\$(VARIETY)\zcoll.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\zmess.exe: $(PFM)\$(VARIETY)\zmess.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\ztfm.exe: $(PFM)\$(VARIETY)\ztfm.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventcnv.exe: $(PFM)\$(VARIETY)\eventcnv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventpy.exe: $(PFM)\$(VARIETY)\eventpy.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventtxt.exe: $(PFM)\$(VARIETY)\eventtxt.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventsql.exe: $(PFM)\$(VARIETY)\eventsql.obj \
|
||||
$(PFM)\$(VARIETY)\sqlite3.obj $(PFM)\$(VARIETY)\mps.lib
|
||||
|
||||
$(PFM)\$(VARIETY)\replay.exe: $(PFM)\$(VARIETY)\replay.obj \
|
||||
$(PFM)\$(VARIETY)\eventrep.obj \
|
||||
$(PFM)\$(VARIETY)\table.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib
|
||||
|
||||
# Have to rename the object file, because the names must match, or
|
||||
# the template rule for .exe.obj won't be used.
|
||||
$(PFM)\$(VARIETY)\replaysw.obj: $(PFM)\$(VARIETY)\replay.obj
|
||||
$(ECHO) $@
|
||||
copy $** $@ >nul:
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventcnv.obj: $(PFM)\$(VARIETY)\eventcnv.obj
|
||||
copy $** $@ >nul:
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventpy.obj: $(PFM)\$(VARIETY)\eventpy.obj
|
||||
copy $** $@ >nul:
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventtxt.obj: $(PFM)\$(VARIETY)\eventtxt.obj
|
||||
copy $** $@ >nul:
|
||||
|
||||
$(PFM)\$(VARIETY)\mpseventsql.obj: $(PFM)\$(VARIETY)\eventsql.obj
|
||||
copy $** $@ >nul:
|
||||
|
||||
!ENDIF
|
||||
|
||||
|
||||
# GENERIC RULES
|
||||
|
||||
# Object files
|
||||
|
||||
{}.c{$(PFM)\$(VARIETY)}.obj:
|
||||
$(ECHO) $@
|
||||
@if not exist $(PFM) mkdir $(PFM)
|
||||
@if not exist $(PFM)\$(VARIETY) mkdir $(PFM)\$(VARIETY)
|
||||
$(CC) /c $(CFLAGS) /Fo$@ $<
|
||||
|
||||
$(PFM)\$(VARIETY)\sqlite3.obj:
|
||||
$(ECHO) $@
|
||||
@if not exist $(PFM) mkdir $(PFM)
|
||||
@if not exist $(PFM)\$(VARIETY) mkdir $(PFM)\$(VARIETY)
|
||||
$(CC) /c $(CFLAGSSQL) /Fo$@ sqlite3.c
|
||||
|
||||
{}.asm{$(PFM)\$(VARIETY)}.obj:
|
||||
$(ECHO) $@
|
||||
@if not exist $(PFM) mkdir $(PFM)
|
||||
@if not exist $(PFM)\$(VARIETY) mkdir $(PFM)\$(VARIETY)
|
||||
$(MASM) /nologo /c /Fo$@ $<
|
||||
|
||||
# Coverage files
|
||||
#{$(PFM)\$(VARIETY)}.exe{$(PFM)\$(VARIETY)}.cov:
|
||||
# $(ECHO) $@
|
||||
# cd $(PFM)\$(VARIETY)
|
||||
# prep /nologo /lv $(<F)
|
||||
# profile /nologo $(<B)
|
||||
# prep /nologo /m $(<B)
|
||||
# plist /nologo /D ..\.. $(<B) > $(@F)
|
||||
|
||||
|
||||
# Executables
|
||||
|
||||
{$(PFM)\$(VARIETY)}.obj{$(PFM)\$(VARIETY)}.exe:
|
||||
$(ECHO) $@
|
||||
$(LINKER) $(LINKFLAGS) /OUT:$@ $(**)
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
363
mps/code/commpre.nmk
Normal file
363
mps/code/commpre.nmk
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
# commpre.nmk: FIRST COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*-1
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# .description: This makefile fragment is included in more specific
|
||||
# makefiles for platforms which use the "mv" builder. This is
|
||||
# the first of two common makefile fragments (the other is commpost.nmk).
|
||||
# Alas, due to shortcomings in nmake, it is not possible to use only one
|
||||
# common fragment.
|
||||
#
|
||||
# %%PART: When adding a new part, add a new parameter for the files included
|
||||
# in the part
|
||||
# Parameters:
|
||||
# PFM platform code, e.g. "w3i3mv"
|
||||
# PFMDEFS /D options to define platforms preprocessor symbols
|
||||
# to the compiler. Avoid using this if possible, as it
|
||||
# prevents the MPS being built with a simple command like
|
||||
# "cl mps.c".
|
||||
# MPMCOMMON list of sources which make up the "mpm" part for all
|
||||
# platforms. Each source is stripped of its .c extension
|
||||
# and surrounded with [brackets].
|
||||
# MPMPF as above for the current platform.
|
||||
# PLINTH as above for the "plinth" part
|
||||
# AMC as above for the "amc" part
|
||||
# AMS as above for the "ams" part
|
||||
# LO as above for the "lo" part
|
||||
# POOLN as above for the "pooln" part
|
||||
# SNC as above for the "snc" part
|
||||
# POOLS as above for all pools included in the target
|
||||
# MPM as above for the MPMCOMMON + MPMPF + PLINTH + POOLS
|
||||
# DW as above for the "dw" part
|
||||
# FMTTEST as above for the "fmttest" part
|
||||
# FMTSCHEME as above for the "fmtscheme" part
|
||||
# TESTLIB as above for the "testlib" part
|
||||
# TESTTHR as above for the "testthr" part
|
||||
# NOISY if defined, causes command to be emitted
|
||||
#
|
||||
#
|
||||
# EDITING
|
||||
#
|
||||
# To add new targets, varieties, and parts:
|
||||
# Search for the string "%%TARGET", "%%VARIETY", or "%%PART" in this makefile
|
||||
# and follow the instructions.
|
||||
#
|
||||
|
||||
|
||||
# TARGETS
|
||||
#
|
||||
#
|
||||
# %%TARGET: When adding a new target, add it to one of the variables
|
||||
# in this section. Library components go in LIB_TARGETS.
|
||||
|
||||
LIB_TARGETS=mps.lib
|
||||
|
||||
# Test cases go in TEST_TARGETS.
|
||||
|
||||
TEST_TARGETS=\
|
||||
abqtest.exe \
|
||||
addrobj.exe \
|
||||
airtest.exe \
|
||||
amcss.exe \
|
||||
amcsshe.exe \
|
||||
amcssth.exe \
|
||||
amsss.exe \
|
||||
amssshe.exe \
|
||||
apss.exe \
|
||||
arenacv.exe \
|
||||
awlut.exe \
|
||||
awluthe.exe \
|
||||
awlutth.exe \
|
||||
btcv.exe \
|
||||
bttest.exe \
|
||||
djbench.exe \
|
||||
extcon.exe \
|
||||
finalcv.exe \
|
||||
finaltest.exe \
|
||||
fotest.exe \
|
||||
gcbench.exe \
|
||||
landtest.exe \
|
||||
locbwcss.exe \
|
||||
lockcov.exe \
|
||||
lockut.exe \
|
||||
locusss.exe \
|
||||
locv.exe \
|
||||
messtest.exe \
|
||||
mpmss.exe \
|
||||
mpsicv.exe \
|
||||
mv2test.exe \
|
||||
nailboardtest.exe \
|
||||
poolncv.exe \
|
||||
qs.exe \
|
||||
sacss.exe \
|
||||
segsmss.exe \
|
||||
sncss.exe \
|
||||
steptest.exe \
|
||||
tagtest.exe \
|
||||
teletest.exe \
|
||||
walkt0.exe \
|
||||
zcoll.exe \
|
||||
zmess.exe \
|
||||
ztfm.exe
|
||||
|
||||
# Stand-alone programs go in EXTRA_TARGETS if they should always be
|
||||
# built, or in OPTIONAL_TARGETS if they should only be built if
|
||||
|
||||
EXTRA_TARGETS=mpseventcnv.exe mpseventpy.exe mpseventtxt.exe
|
||||
OPTIONAL_TARGETS=mpseventsql.exe
|
||||
|
||||
# This target records programs that we were once able to build but
|
||||
# can't at the moment:
|
||||
#
|
||||
# replay -- depends on the EPVM pool.
|
||||
|
||||
UNBUILDABLE_TARGETS=replay.exe
|
||||
|
||||
ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS)
|
||||
|
||||
|
||||
# PARAMETERS
|
||||
#
|
||||
#
|
||||
# %%PART: When adding a new part, add the sources for the new part here.
|
||||
|
||||
MPMCOMMON=\
|
||||
[abq] \
|
||||
[arena] \
|
||||
[arenacl] \
|
||||
[arenavm] \
|
||||
[arg] \
|
||||
[boot] \
|
||||
[bt] \
|
||||
[buffer] \
|
||||
[cbs] \
|
||||
[dbgpool] \
|
||||
[dbgpooli] \
|
||||
[event] \
|
||||
[failover] \
|
||||
[format] \
|
||||
[freelist] \
|
||||
[global] \
|
||||
[land] \
|
||||
[ld] \
|
||||
[locus] \
|
||||
[message] \
|
||||
[meter] \
|
||||
[mpm] \
|
||||
[mpsi] \
|
||||
[nailboard] \
|
||||
[policy] \
|
||||
[pool] \
|
||||
[poolabs] \
|
||||
[poolmfs] \
|
||||
[poolmrg] \
|
||||
[poolmv2] \
|
||||
[protocol] \
|
||||
[range] \
|
||||
[rangetree] \
|
||||
[ref] \
|
||||
[ring] \
|
||||
[root] \
|
||||
[sa] \
|
||||
[sac] \
|
||||
[scan] \
|
||||
[seg] \
|
||||
[shield] \
|
||||
[splay] \
|
||||
[ss] \
|
||||
[table] \
|
||||
[trace] \
|
||||
[traceanc] \
|
||||
[tract] \
|
||||
[trans] \
|
||||
[tree] \
|
||||
[version] \
|
||||
[vm] \
|
||||
[walk]
|
||||
PLINTH = [mpsliban] [mpsioan]
|
||||
AMC = [poolamc]
|
||||
AMS = [poolams]
|
||||
AWL = [poolawl]
|
||||
LO = [poollo]
|
||||
MVFF = [poolmvff]
|
||||
POOLN = [pooln]
|
||||
SNC = [poolsnc]
|
||||
FMTDY = [fmtdy] [fmtno]
|
||||
FMTTEST = [fmthe] [fmtdy] [fmtno] [fmtdytst]
|
||||
FMTSCHEME = [fmtscheme]
|
||||
TESTLIB = [testlib] [getoptl]
|
||||
TESTTHR = [testthrw3]
|
||||
POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC)
|
||||
MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH)
|
||||
|
||||
|
||||
# CHECK PARAMETERS
|
||||
#
|
||||
#
|
||||
# %%PART: When adding a new part, add checks for the parameter with the
|
||||
# sources for the new part.
|
||||
|
||||
!IFNDEF PFM
|
||||
!ERROR commpre.nmk: PFM not defined
|
||||
!ENDIF
|
||||
!IFNDEF MPM
|
||||
!ERROR commpre.nmk: MPM not defined
|
||||
!ENDIF
|
||||
!IFNDEF MPMCOMMON
|
||||
!ERROR commpre.nmk: MPMCOMMON not defined
|
||||
!ENDIF
|
||||
!IFNDEF MPMPF
|
||||
!ERROR commpre.nmk: MPMPF not defined
|
||||
!ENDIF
|
||||
!IFNDEF PLINTH
|
||||
!ERROR commpre.nmk: PLINTH not defined
|
||||
!ENDIF
|
||||
!IFNDEF LO
|
||||
!ERROR commpre.nmk: LO not defined
|
||||
!ENDIF
|
||||
!IFNDEF AMC
|
||||
!ERROR commpre.nmk: AMC not defined
|
||||
!ENDIF
|
||||
!IFNDEF AMS
|
||||
!ERROR commpre.nmk: AMS not defined
|
||||
!ENDIF
|
||||
!IFNDEF POOLN
|
||||
!ERROR commpre.nmk: POOLN not defined
|
||||
!ENDIF
|
||||
!IFNDEF SNC
|
||||
!ERROR commpre.nmk: SNC not defined
|
||||
!ENDIF
|
||||
!IFNDEF FMTDY
|
||||
!ERROR commpre.nmk: FMTDY not defined
|
||||
!ENDIF
|
||||
!IFNDEF FMTTEST
|
||||
!ERROR commpre.nmk: FMTTEST not defined
|
||||
!ENDIF
|
||||
!IFNDEF FMTSCHEME
|
||||
!ERROR commpre.nmk: FMTSCHEME not defined
|
||||
!ENDIF
|
||||
!IFNDEF TESTLIB
|
||||
!ERROR commpre.nmk: TESTLIB not defined
|
||||
!ENDIF
|
||||
!IFNDEF TESTTHR
|
||||
!ERROR commpre.nmk: TESTTHR not defined
|
||||
!ENDIF
|
||||
|
||||
|
||||
# DECLARATIONS
|
||||
|
||||
|
||||
!IFDEF NOISY
|
||||
ECHO = rem
|
||||
!ELSE
|
||||
.SILENT:
|
||||
ECHO = echo
|
||||
!ENDIF
|
||||
|
||||
|
||||
# C FLAGS
|
||||
|
||||
CFLAGSTARGETPRE =
|
||||
CFLAGSTARGETPOST =
|
||||
CRTFLAGSHOT =
|
||||
CRTFLAGSCOOL =
|
||||
LINKFLAGSHOT =
|
||||
LINKFLAGSCOOL =
|
||||
|
||||
CFLAGSSQLPRE = /nologo $(PFMDEFS)
|
||||
CFLAGSCOMMONPRE = /nologo $(PFMDEFS) $(CFLAGSTARGETPRE)
|
||||
CFLAGSSQLPOST =
|
||||
CFLAGSCOMMONPOST = $(CFLAGSTARGETPOST)
|
||||
|
||||
# Flags for use in the variety combinations
|
||||
CFLAGSHOT = /O2
|
||||
# (above /O2 (maximise speed) used to be set to /Ox
|
||||
# (maximise optimisations) in for tool versions before VS 9)
|
||||
# We used to have /GZ here (stack probe).
|
||||
# Note that GZ is specific to version 12 of the cl tool. drj 2003-11-04
|
||||
# It is ignored on earlier versions of the cl tool.
|
||||
# /GZ here generates a dependency on the C library and when we are
|
||||
# building a DLL, mpsdy.dll, the linker step will fail (error LNK2001:
|
||||
# unresolved external symbol __chkesp). See
|
||||
# http://support.microsoft.com/kb/q191669/
|
||||
CFLAGSCOOL =
|
||||
CFLAGSINTERNAL = /Zi
|
||||
CFLAGSEXTERNAL =
|
||||
|
||||
# The combinations of variety
|
||||
# %%VARIETY: When adding a new variety, define a macro containing the set
|
||||
# of flags for the new variety.
|
||||
CFRASH = /DCONFIG_VAR_RASH $(CRTFLAGSHOT) $(CFLAGSHOT) $(CFLAGSEXTERNAL)
|
||||
CFHOT = /DCONFIG_VAR_HOT $(CRTFLAGSHOT) $(CFLAGSHOT) $(CFLAGSINTERNAL)
|
||||
CFCOOL = /DCONFIG_VAR_COOL $(CRTFLAGSCOOL) $(CFLAGSCOOL) $(CFLAGSINTERNAL)
|
||||
|
||||
# Microsoft documentation is not very clear on the point of using both
|
||||
# optimization and debug information
|
||||
|
||||
# LINKER FLAGS
|
||||
# %%VARIETY: When adding a new variety, define a macro containing the flags
|
||||
# for the new variety
|
||||
LINKER = link
|
||||
LINKFLAGSCOMMON = /nologo /LARGEADDRESSAWARE
|
||||
LINKFLAGSINTERNAL = /DEBUG
|
||||
# ( Internal flags used to be set to /DEBUG:full )
|
||||
LINKFLAGSEXTERNAL = /RELEASE
|
||||
|
||||
LFRASH = $(LINKFLAGSHOT) $(LINKFLAGSEXTERNAL)
|
||||
LFHOT = $(LINKFLAGSHOT) $(LINKFLAGSINTERNAL)
|
||||
LFCOOL = $(LINKFLAGSCOOL) $(LINKFLAGSINTERNAL)
|
||||
|
||||
#LFCV = /PROFILE /DEBUG:full /DEBUGTYPE:cv
|
||||
|
||||
# Library manager
|
||||
# %%VARIETY: When adding a new variety, define a macro containing the flags
|
||||
# for the new variety
|
||||
LIBMAN = lib # can't call this LIB - it screws the environment
|
||||
LIBFLAGSCOMMON =
|
||||
|
||||
LIBFLAGSRASH =
|
||||
LIBFLAGSHOT =
|
||||
LIBFLAGSCOOL =
|
||||
|
||||
# Browser database manager [not used at present]
|
||||
#BSC = bscmake
|
||||
#BSCFLAGS = /nologo /n
|
||||
|
||||
|
||||
# == Common definitions ==
|
||||
# %%PART: When adding a new part, add it here, unless it's platform-specific
|
||||
# [It is not possible use a macro, like $(PFM), in a substitution,
|
||||
# hence all parts end up being platform-specific.]
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
768
mps/code/config.h
Normal file
768
mps/code/config.h
Normal file
|
|
@ -0,0 +1,768 @@
|
|||
/* config.h: MPS CONFIGURATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (c) 2002 Global Graphics Software.
|
||||
*
|
||||
* PURPOSE
|
||||
*
|
||||
* This module translates from high-level symbols defined by the
|
||||
* external build system (gnumake, nmake, etc.) into specific sets
|
||||
* of features used by MPS modules.
|
||||
*
|
||||
* TODO: It also defines defaults for various parts of the MPS.
|
||||
* Perhaps this should be split into a separate header with better
|
||||
* documentation. See
|
||||
* <https://github.com/Ravenbrook/mps/issues/176#issuecomment-1443192870>.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* <design/config>.
|
||||
*
|
||||
* TODO: Many of the default constants defined in this file do not
|
||||
* have documented justification. See GitHub issue #176
|
||||
* <https://github.com/Ravenbrook/mps/issues/176>.
|
||||
*/
|
||||
|
||||
#ifndef config_h
|
||||
#define config_h
|
||||
|
||||
|
||||
/* CONFIG_VAR_* -- variety Configuration
|
||||
*
|
||||
* These are translated into the directives CONFIG_ASSERT, CONFIG_STATS,
|
||||
* CONFIG_LOG, etc. which control actual compilation features.
|
||||
*/
|
||||
|
||||
/* CONFIG_VAR_RASH -- the rash and reckless variety
|
||||
*
|
||||
* This variety switches off as many features as possible for maximum
|
||||
* performance, but is therefore unsafe and undebuggable. It is not intended
|
||||
* for use, but for comparison with the hot variety, to check that assertion,
|
||||
* logging, etc. have negligible overhead.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_VAR_RASH)
|
||||
/* no asserts */
|
||||
/* no statistic meters */
|
||||
/* no telemetry log events */
|
||||
|
||||
|
||||
/* CONFIG_VAR_COOL -- cool variety
|
||||
*
|
||||
* The cool variety is intended for use when developing an integration with
|
||||
* the MPS or debugging memory problems or collecting detailed telemetry
|
||||
* data for performance analysis. It has more thorough consistency checking
|
||||
* and data collection and output, and full debugging information.
|
||||
*/
|
||||
|
||||
#elif defined(CONFIG_VAR_COOL)
|
||||
#define CONFIG_ASSERT
|
||||
#define CONFIG_ASSERT_ALL
|
||||
#define CONFIG_ASSERT_ABORT
|
||||
#define CONFIG_STATS
|
||||
#ifndef CHECKLEVEL
|
||||
#define CHECKLEVEL CheckLevelSHALLOW
|
||||
#endif
|
||||
#define CONFIG_LOG
|
||||
#define CONFIG_LOG_ALL
|
||||
|
||||
|
||||
#else /* CONFIG_VAR_* */
|
||||
|
||||
/* CONFIG_VAR_HOT -- the hot variety
|
||||
*
|
||||
* This variety is the default variety for distribution in products that use
|
||||
* the MPS. It has maximum performance while retaining a good level of
|
||||
* consistency checking and allowing some debugging and telemetry features.
|
||||
*/
|
||||
|
||||
/* #elif defined(CONFIG_VAR_HOT) */
|
||||
#define CONFIG_ASSERT
|
||||
/* Note, not CONFIG_ASSERT_ABORT */
|
||||
#ifndef CHECKLEVEL
|
||||
#define CHECKLEVEL CheckLevelMINIMAL
|
||||
#endif
|
||||
/* no statistic meters */
|
||||
#define CONFIG_LOG
|
||||
|
||||
#endif /* CONFIG_VAR_* */
|
||||
|
||||
|
||||
/* Build Features */
|
||||
|
||||
|
||||
#if defined(CONFIG_ASSERT)
|
||||
/* asserts: AVER, AVERT, NOTREACHED, CHECKx */
|
||||
/* note: a direct call to ASSERT() will *still* fire */
|
||||
#define AVER_AND_CHECK
|
||||
#if defined(CONFIG_ASSERT_ALL)
|
||||
#define AVER_AND_CHECK_ALL
|
||||
#define MPS_ASSERT_STRING "assertastic"
|
||||
#else /* CONFIG_ASSERT_ALL, not */
|
||||
#define MPS_ASSERT_STRING "asserted"
|
||||
#endif /* CONFIG_ASSERT_ALL */
|
||||
#else /* CONFIG_ASSERT, not */
|
||||
#define AVER_AND_CHECK_NONE
|
||||
#define MPS_ASSERT_STRING "nonasserted"
|
||||
#endif
|
||||
#if defined(CONFIG_ASSERT_ABORT)
|
||||
#define ASSERT_ABORT() abort()
|
||||
#else
|
||||
#define ASSERT_ABORT() NOOP
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_STATS)
|
||||
/* CONFIG_STATS = STATISTICS = METERs */
|
||||
#define STATISTICS
|
||||
#define MPS_STATS_STRING "stats"
|
||||
#else
|
||||
#define STATISTICS_NONE
|
||||
#define MPS_STATS_STRING "nonstats"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CONFIG_LOG)
|
||||
/* TELEMETRY = LOG = EVENTs */
|
||||
#define EVENT
|
||||
#if defined(CONFIG_LOG_ALL)
|
||||
#define EVENT_ALL 1 /* log events on critical path */
|
||||
#define MPS_LOG_STRING "logtastic"
|
||||
#else /* CONFIG_LOG_ALL, not */
|
||||
#define EVENT_ALL 0 /* don't log events on critical path */
|
||||
#define MPS_LOG_STRING "logging"
|
||||
#endif /* CONFIG_LOG_ALL */
|
||||
#else /* CONFIG_LOG, not */
|
||||
#define EVENT_NONE
|
||||
#define MPS_LOG_STRING "nonlogging"
|
||||
#endif /* CONFIG_LOG */
|
||||
|
||||
|
||||
/* CONFIG_PLINTH_NONE -- exclude the ANSI plinth
|
||||
*
|
||||
* Some MPS deployment environments want to avoid dependencies on the
|
||||
* standard C library. In this case, the plinth, defined in mpslib.h must
|
||||
* be supplied when linking.
|
||||
*
|
||||
* For example, Open Dylan on Windows does not link the C library, but
|
||||
* supplies its own plinth directly using Windows and Dylan interfaces.
|
||||
*
|
||||
* CONFIG_PLINTH_NONE tells mps.c to exclude the ANSI plinth and removes
|
||||
* all standard C library dependencies. e.g.
|
||||
*
|
||||
* cc -O2 -c -DCONFIG_PLINTH_NONE mps.c
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_PLINTH_NONE)
|
||||
#define PLINTH
|
||||
#else
|
||||
#define PLINTH_NONE
|
||||
#endif
|
||||
|
||||
|
||||
/* CONFIG_PF_ANSI -- use the ANSI platform
|
||||
*
|
||||
* This symbol tells mps.c to exclude the sources for the
|
||||
* auto-detected platform, and use the generic ("ANSI") platform
|
||||
* instead.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_PF_ANSI)
|
||||
#define PLATFORM_ANSI
|
||||
#endif
|
||||
|
||||
|
||||
/* CONFIG_THREAD_SINGLE -- support single-threaded execution only
|
||||
*
|
||||
* This symbol causes the MPS to be built for single-threaded
|
||||
* execution only, where locks are not needed and so the generic
|
||||
* ("ANSI") lock module lockan.c can be used instead of the
|
||||
* platform-specific lock module.
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_THREAD_SINGLE)
|
||||
#define LOCK
|
||||
#else
|
||||
#define LOCK_NONE
|
||||
#endif
|
||||
|
||||
|
||||
/* CONFIG_POLL_NONE -- no support for polling
|
||||
*
|
||||
* This symbol causes the MPS to built without support for polling.
|
||||
* This means that garbage collections will only happen if requested
|
||||
* explicitly via mps_arena_collect() or mps_arena_step(), but it also
|
||||
* means that protection is not needed, and so shield operations can
|
||||
* be replaced with no-ops in mpm.h.
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_POLL_NONE)
|
||||
#define REMEMBERED_SET
|
||||
#define SHIELD
|
||||
#else
|
||||
#if !defined(CONFIG_THREAD_SINGLE)
|
||||
#error "CONFIG_POLL_NONE without CONFIG_THREAD_SINGLE"
|
||||
#endif
|
||||
#define REMEMBERED_SET_NONE
|
||||
#define SHIELD_NONE
|
||||
#endif
|
||||
|
||||
|
||||
#define MPS_VARIETY_STRING \
|
||||
MPS_ASSERT_STRING "." MPS_LOG_STRING "." MPS_STATS_STRING
|
||||
|
||||
|
||||
/* Platform Configuration */
|
||||
|
||||
#include "mpstd.h"
|
||||
|
||||
/* Suppress Visual C warnings at /W4 (warning level 4) */
|
||||
/* This is also done in testlib.h. */
|
||||
|
||||
#ifdef MPS_BUILD_MV
|
||||
|
||||
/* "constant conditional" (provoked by MPS_END) */
|
||||
#pragma warning(disable: 4127)
|
||||
|
||||
#endif /* MPS_BUILD_MV */
|
||||
|
||||
|
||||
/* Suppress Pelles C warnings at /W2 (warning level 2) */
|
||||
/* Some of the same settings are done in testlib.h. */
|
||||
|
||||
#ifdef MPS_BUILD_PC
|
||||
|
||||
/* "Unreachable code" (provoked by AVER, if condition is constantly true). */
|
||||
#pragma warn(disable: 2154)
|
||||
|
||||
/* "Consider changing type to 'size_t' for loop variable" */
|
||||
#pragma warn(disable: 2804)
|
||||
|
||||
#endif /* MPS_BUILD_PC */
|
||||
|
||||
|
||||
/* MPS_FILE -- expands to __FILE__ in nested macros */
|
||||
|
||||
#ifdef MPS_BUILD_PC
|
||||
|
||||
/* Pelles C loses definition of __FILE__ in deeply nested macro
|
||||
* expansions. See <https://forum.pellesc.de/index.php?topic=5474.0>
|
||||
*/
|
||||
#define MPS_FILE "<__FILE__ unavailable in " MPS_PF_STRING ">"
|
||||
|
||||
#else
|
||||
|
||||
#define MPS_FILE __FILE__
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Function attributes */
|
||||
/* Some of these are also defined in testlib.h */
|
||||
|
||||
/* Attribute for functions that take a printf-like format argument, so
|
||||
* that the compiler can check the format specifiers against the types
|
||||
* of the arguments.
|
||||
* GCC: <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-format-function-attribute>
|
||||
* Clang: <https://clang.llvm.org/docs/AttributeReference.html#format-gnu-format>
|
||||
*/
|
||||
#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
|
||||
#define ATTRIBUTE_FORMAT(ARGLIST) __attribute__((__format__ ARGLIST))
|
||||
#else
|
||||
#define ATTRIBUTE_FORMAT(ARGLIST)
|
||||
#endif
|
||||
|
||||
/* Attribute for functions that should not be instrumented by Clang's
|
||||
* address sanitizer.
|
||||
* <https://clang.llvm.org/docs/AddressSanitizer.html#attribute-no-sanitize-address>
|
||||
*/
|
||||
#if defined(MPS_BUILD_LL)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((__no_sanitize_address__))
|
||||
#else
|
||||
#define ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||
#endif
|
||||
#else
|
||||
#define ATTRIBUTE_NO_SANITIZE_ADDRESS
|
||||
#endif
|
||||
|
||||
/* Attribute for functions that must not be inlined.
|
||||
* GCC: <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noinline-function-attribute>
|
||||
* MSVC: <https://docs.microsoft.com/en-us/cpp/cpp/noinline>
|
||||
*/
|
||||
#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
|
||||
#define ATTRIBUTE_NOINLINE __attribute__((__noinline__))
|
||||
#elif defined(MPS_BUILD_MV)
|
||||
#define ATTRIBUTE_NOINLINE __declspec(noinline)
|
||||
#else
|
||||
#define ATTRIBUTE_NOINLINE
|
||||
#endif
|
||||
|
||||
/* Attribute for functions that do not return.
|
||||
* GCC: <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noreturn-function-attribute>
|
||||
* Clang: <https://clang.llvm.org/docs/AttributeReference.html#id1>
|
||||
*/
|
||||
#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
|
||||
#define ATTRIBUTE_NORETURN __attribute__((__noreturn__))
|
||||
#else
|
||||
#define ATTRIBUTE_NORETURN
|
||||
#endif
|
||||
|
||||
/* Attribute for functions that may be unused in some build configurations.
|
||||
* GCC: <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-unused-function-attribute>
|
||||
*
|
||||
* This attribute must be applied to all Check functions, otherwise
|
||||
* the RASH variety fails to compile with -Wunused-function. (It
|
||||
* should not be applied to functions that are unused in all build
|
||||
* configurations: these functions should not be compiled.)
|
||||
*/
|
||||
#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
|
||||
#define ATTRIBUTE_UNUSED __attribute__((__unused__))
|
||||
#else
|
||||
#define ATTRIBUTE_UNUSED
|
||||
#endif
|
||||
|
||||
|
||||
/* Compiler extensions */
|
||||
|
||||
/* LIKELY -- likely conditions
|
||||
*
|
||||
* Use to annotate conditions that are likely to be true, such as
|
||||
* assertions, to help move unlikely code out-of-line. See
|
||||
* <https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html>.
|
||||
*/
|
||||
|
||||
#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL)
|
||||
#define LIKELY(exp) __builtin_expect((exp) != 0, 1)
|
||||
#else
|
||||
#define LIKELY(exp) ((exp) != 0)
|
||||
#endif
|
||||
|
||||
|
||||
/* Buffer Configuration -- see <code/buffer.c> */
|
||||
|
||||
#define BUFFER_RANK_DEFAULT (mps_rank_exact())
|
||||
|
||||
|
||||
/* Format defaults: see <code/format.c> */
|
||||
|
||||
#define FMT_ALIGN_DEFAULT ((Align)MPS_PF_ALIGN)
|
||||
#define FMT_HEADER_SIZE_DEFAULT ((Size)0)
|
||||
#define FMT_SCAN_DEFAULT (&FormatNoScan)
|
||||
#define FMT_SKIP_DEFAULT (&FormatNoSkip)
|
||||
#define FMT_FWD_DEFAULT (&FormatNoMove)
|
||||
#define FMT_ISFWD_DEFAULT (&FormatNoIsMoved)
|
||||
#define FMT_PAD_DEFAULT (&FormatNoPad)
|
||||
#define FMT_CLASS_DEFAULT (&FormatDefaultClass)
|
||||
|
||||
|
||||
/* Pool AMC Configuration -- see <code/poolamc.c> */
|
||||
|
||||
#define AMC_INTERIOR_DEFAULT TRUE
|
||||
/* AMC treats objects larger than or equal to this as "Large" */
|
||||
#define AMC_LARGE_SIZE_DEFAULT ((Size)32768)
|
||||
#define AMC_EXTEND_BY_DEFAULT ((Size)8192)
|
||||
|
||||
|
||||
/* Pool AMS Configuration -- see <code/poolams.c> */
|
||||
|
||||
#define AMS_SUPPORT_AMBIGUOUS_DEFAULT TRUE
|
||||
#define AMS_GEN_DEFAULT 0
|
||||
|
||||
|
||||
/* Pool AWL Configuration -- see <code/poolawl.c> */
|
||||
|
||||
#define AWL_GEN_DEFAULT 0
|
||||
#define AWL_HAVE_SEG_SA_LIMIT TRUE
|
||||
#define AWL_SEG_SA_LIMIT 200 /* TODO: Improve guesswork with measurements */
|
||||
#define AWL_HAVE_TOTAL_SA_LIMIT FALSE
|
||||
#define AWL_TOTAL_SA_LIMIT 0
|
||||
|
||||
|
||||
/* Pool LO Configuration -- see <code/poollo.c> */
|
||||
|
||||
#define LO_GEN_DEFAULT 0
|
||||
|
||||
|
||||
/* Pool MFS Configuration -- see <code/poolmfs.c> */
|
||||
|
||||
#define MFS_EXTEND_BY_DEFAULT ((Size)65536)
|
||||
|
||||
|
||||
/* Pool MVFF Configuration -- see <code/poolmvff.c> */
|
||||
|
||||
#define MVFF_EXTEND_BY_DEFAULT ((Size)65536)
|
||||
#define MVFF_AVG_SIZE_DEFAULT ((Size)32)
|
||||
#define MVFF_ALIGN_DEFAULT MPS_PF_ALIGN
|
||||
#define MVFF_SLOT_HIGH_DEFAULT FALSE
|
||||
#define MVFF_ARENA_HIGH_DEFAULT FALSE
|
||||
#define MVFF_FIRST_FIT_DEFAULT TRUE
|
||||
#define MVFF_SPARE_DEFAULT 0.75
|
||||
|
||||
|
||||
/* Pool MVT Configuration -- see <code/poolmv2.c> */
|
||||
|
||||
/* TODO: These numbers were lifted from mv2test and need thought. See
|
||||
GitHub issue #176
|
||||
<https://github.com/Ravenbrook/mps/issues/176>. */
|
||||
|
||||
#define MVT_ALIGN_DEFAULT MPS_PF_ALIGN
|
||||
#define MVT_MIN_SIZE_DEFAULT MPS_PF_ALIGN
|
||||
#define MVT_MEAN_SIZE_DEFAULT 32
|
||||
#define MVT_MAX_SIZE_DEFAULT 8192
|
||||
#define MVT_RESERVE_DEPTH_DEFAULT 1024
|
||||
#define MVT_FRAG_LIMIT_DEFAULT 30
|
||||
|
||||
|
||||
/* Arena Configuration -- see <code/arena.c> */
|
||||
|
||||
#define ArenaPollALLOCTIME (65536.0)
|
||||
|
||||
/* .client.seg-size: ARENA_CLIENT_GRAIN_SIZE is the minimum size, in
|
||||
* bytes, of a grain in the client arena. It's set at 8192 with no
|
||||
* particular justification. */
|
||||
|
||||
#define ARENA_CLIENT_GRAIN_SIZE ((Size)8192)
|
||||
|
||||
#define ARENA_DEFAULT_COMMIT_LIMIT ((Size)-1)
|
||||
|
||||
#define ARENA_SPARE_DEFAULT 0.75
|
||||
|
||||
/* ARENA_DEFAULT_PAUSE_TIME is the maximum time (in seconds) that
|
||||
* operations within the arena may pause the mutator for. The default
|
||||
* is set for typical human interaction. See mps_arena_pause_time_set
|
||||
* in the manual. */
|
||||
|
||||
#define ARENA_DEFAULT_PAUSE_TIME (0.1)
|
||||
|
||||
#define ARENA_DEFAULT_ZONED TRUE
|
||||
|
||||
/* ARENA_MINIMUM_COLLECTABLE_SIZE is the minimum size (in bytes) of
|
||||
* collectable memory that might be considered worthwhile to run a
|
||||
* full garbage collection. */
|
||||
|
||||
#define ARENA_MINIMUM_COLLECTABLE_SIZE ((Size)1000000)
|
||||
|
||||
/* ARENA_DEFAULT_COLLECTION_RATE is an estimate of the MPS's
|
||||
* collection rate (in work per second; see <design/type#.work>), for
|
||||
* use in the case where there isn't enough data to use a measured
|
||||
* value. */
|
||||
|
||||
#define ARENA_DEFAULT_COLLECTION_RATE (25000000.0)
|
||||
|
||||
/* ARENA_DEFAULT_COLLECTION_OVERHEAD is an estimate of the MPS's
|
||||
* collection overhead (in seconds), for use in the case where there
|
||||
* isn't enough data to use a measured value. */
|
||||
|
||||
#define ARENA_DEFAULT_COLLECTION_OVERHEAD (0.1)
|
||||
|
||||
/* ARENA_MAX_COLLECT_FRACTION is the maximum fraction of runtime that
|
||||
* ArenaStep is prepared to spend in collections. */
|
||||
|
||||
#define ARENA_MAX_COLLECT_FRACTION (0.1)
|
||||
|
||||
/* ArenaDefaultZONESET is the zone set used by LocusPrefDEFAULT.
|
||||
*
|
||||
* TODO: This is left over from before branches 2014-01-29/mps-chain-zones
|
||||
* and 2014-01-17/cbs-tract-alloc reformed allocation, and may now be
|
||||
* doing more harm than good. Experiment with setting to ZoneSetUNIV. */
|
||||
|
||||
#define ArenaDefaultZONESET (ZoneSetUNIV << (MPS_WORD_WIDTH / 2))
|
||||
|
||||
/* LocusPrefDEFAULT is the allocation preference used by manual pool
|
||||
* classes (these don't care where they allocate). */
|
||||
|
||||
#define LocusPrefDEFAULT { \
|
||||
LocusPrefSig, /* sig */ \
|
||||
FALSE, /* high */ \
|
||||
ArenaDefaultZONESET, /* zoneSet */ \
|
||||
ZoneSetEMPTY, /* avoid */ \
|
||||
}
|
||||
|
||||
#define LDHistoryLENGTH ((Size)4)
|
||||
|
||||
/* Value of MPS_KEY_EXTEND_BY for the arena control pool. */
|
||||
#define CONTROL_EXTEND_BY ((Size)32768)
|
||||
|
||||
#define VM_ARENA_SIZE_DEFAULT ((Size)1 << 28)
|
||||
|
||||
|
||||
/* Locus configuration -- see <code/locus.c> */
|
||||
|
||||
/* Weighting for the current observation, in the exponential moving
|
||||
* average computation of the mortality of a generation. */
|
||||
#define LocusMortalityALPHA (0.4)
|
||||
|
||||
|
||||
/* Stack probe configuration -- see <code/sp*.c> */
|
||||
|
||||
/* Currently StackProbe has a useful implementation only on Windows. */
|
||||
#if defined(MPS_OS_W3) && !defined(CONFIG_PF_ANSI)
|
||||
/* See <design/sp#.sol.depth.analysis> for a justification of this value. */
|
||||
#define StackProbeDEPTH ((Size)500)
|
||||
#else
|
||||
#define StackProbeDEPTH ((Size)0)
|
||||
#endif
|
||||
|
||||
|
||||
/* Shield Configuration -- see <code/shield.c> */
|
||||
|
||||
#define ShieldQueueLENGTH 512 /* initial length of shield queue */
|
||||
#define ShieldDepthWIDTH 4 /* log2(max nested exposes + 1) */
|
||||
|
||||
|
||||
/* VM Configuration -- see <code/vm*.c> */
|
||||
|
||||
#define VMAN_PAGE_SIZE ((Align)4096)
|
||||
#define VMJunkBYTE ((unsigned char)0xA9)
|
||||
#define VMParamSize (sizeof(Word))
|
||||
|
||||
|
||||
/* .feature.li: Linux feature specification
|
||||
*
|
||||
* The MPS needs the following symbols which are not defined if the
|
||||
* -ansi option is given to GCC:
|
||||
*
|
||||
* Source Symbols Header Feature
|
||||
* =========== ========================= ============= ====================
|
||||
* eventtxt.c setenv <stdlib.h> _GNU_SOURCE
|
||||
* lockix.c pthread_mutexattr_settype <pthread.h> _XOPEN_SOURCE >= 500
|
||||
* prmcix.h stack_t, siginfo_t <signal.h> _XOPEN_SOURCE
|
||||
* prmclii3.c REG_EAX etc. <ucontext.h> _GNU_SOURCE
|
||||
* prmclii6.c REG_RAX etc. <ucontext.h> _GNU_SOURCE
|
||||
* pthrdext.c sigaction etc. <signal.h> _XOPEN_SOURCE
|
||||
* vmix.c MAP_ANON <sys/mman.h> _GNU_SOURCE
|
||||
*
|
||||
* It is not possible to localize these feature specifications around
|
||||
* the individual headers: all headers share a common set of features
|
||||
* (via <feature.h>) and so all sources in the same compilation unit
|
||||
* must turn on the same set of features.
|
||||
*
|
||||
* See "Feature Test Macros" in the Glibc Manual:
|
||||
* <https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html>
|
||||
*/
|
||||
|
||||
#if defined(MPS_OS_LI)
|
||||
|
||||
#if defined(_XOPEN_SOURCE) && _XOPEN_SOURCE < 500
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
#if !defined(_XOPEN_SOURCE)
|
||||
#define _XOPEN_SOURCE 500
|
||||
#endif
|
||||
|
||||
#if !defined(_GNU_SOURCE)
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* .feature.xc: macOS feature specification
|
||||
*
|
||||
* The MPS needs the following symbols which are not defined by default
|
||||
*
|
||||
* Source Symbols Header Feature
|
||||
* =========== ========================= ============= ====================
|
||||
* prmclii3.c __eax etc. <ucontext.h> _XOPEN_SOURCE
|
||||
* prmclii6.c __rax etc. <ucontext.h> _XOPEN_SOURCE
|
||||
*
|
||||
* It is not possible to localize these feature specifications around
|
||||
* the individual headers: all headers share a common set of features
|
||||
* (via <sys/cdefs.h>) and so all sources in the same compilation unit
|
||||
* must turn on the same set of features.
|
||||
*/
|
||||
|
||||
#if defined(MPS_OS_XC)
|
||||
|
||||
#if !defined(_XOPEN_SOURCE)
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Almost all of protxc.c etc. are architecture-independent, but unfortunately
|
||||
the Mach headers don't provide architecture neutral symbols for simple
|
||||
things like thread states. These definitions fix that. */
|
||||
|
||||
#if defined(MPS_OS_XC)
|
||||
#if defined(MPS_ARCH_A6)
|
||||
|
||||
#define THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE_FLAVOR ARM_THREAD_STATE64
|
||||
#define THREAD_STATE_S arm_thread_state64_t
|
||||
|
||||
#elif defined(MPS_ARCH_I6)
|
||||
|
||||
#define THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE_FLAVOR x86_THREAD_STATE64
|
||||
#define THREAD_STATE_S x86_thread_state64_t
|
||||
|
||||
#elif defined(MPS_ARCH_I3)
|
||||
|
||||
#define THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
|
||||
#define THREAD_STATE_FLAVOR x86_THREAD_STATE32
|
||||
#define THREAD_STATE_S x86_thread_state32_t
|
||||
|
||||
#else
|
||||
|
||||
#error "Unknown macOS architecture"
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* POSIX thread extensions configuration -- see <code/pthrdext.c> */
|
||||
|
||||
#if defined(MPS_OS_LI) || defined(MPS_OS_FR)
|
||||
|
||||
/* PTHREADEXT_SIGSUSPEND -- signal used to suspend a thread
|
||||
* <design/pthreadext#.impl.signals>
|
||||
*/
|
||||
#if defined(CONFIG_PTHREADEXT_SIGSUSPEND)
|
||||
#define PTHREADEXT_SIGSUSPEND CONFIG_PTHREADEXT_SIGSUSPEND
|
||||
#else
|
||||
#define PTHREADEXT_SIGSUSPEND SIGXFSZ
|
||||
#endif
|
||||
|
||||
/* PTHREADEXT_SIGRESUME -- signal used to resume a thread
|
||||
* <design/pthreadext#.impl.signals>
|
||||
*/
|
||||
#if defined(CONFIG_PTHREADEXT_SIGRESUME)
|
||||
#define PTHREADEXT_SIGRESUME CONFIG_PTHREADEXT_SIGRESUME
|
||||
#else
|
||||
#define PTHREADEXT_SIGRESUME SIGXCPU
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Tracer Configuration -- see <code/trace.c> */
|
||||
|
||||
#define TraceLIMIT ((size_t)1)
|
||||
/* I count 4 function calls to scan, 10 to copy. */
|
||||
#define TraceCopyScanRATIO (1.5)
|
||||
|
||||
|
||||
/* Events
|
||||
*
|
||||
* EventBufferSIZE is the number of words in the global event buffer.
|
||||
*/
|
||||
|
||||
#define EventBufferSIZE ((size_t)4096)
|
||||
#define EventStringLengthMAX ((size_t)255) /* Not including NUL */
|
||||
|
||||
|
||||
/* Assert Buffer */
|
||||
|
||||
#define ASSERT_BUFFER_SIZE ((Size)512)
|
||||
|
||||
|
||||
/* memory operator configuration
|
||||
*
|
||||
* We need efficient operators similar to memcpy, memset, and memcmp.
|
||||
* In general, we cannot use the C library mem functions directly as
|
||||
* that would not be freestanding. However, on some platforms we can do
|
||||
* this, because they are inlined by the compiler and so do not actually
|
||||
* create a dependence on an external library.
|
||||
*/
|
||||
|
||||
#if defined(MPS_PF_W3I3MV)
|
||||
/* MSVC on Intel inlines mem* when optimizing */
|
||||
#define mps_lib_memset(p, c, l) memset(p, c, l)
|
||||
#define mps_lib_memcpy(p, q, s) memcpy(p, q, s)
|
||||
#define mps_lib_memcmp(p, q, s) memcmp(p, q, s)
|
||||
/* get prototypes for ANSI mem* */
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* Product Configuration
|
||||
*
|
||||
* Deprecated, see design/config/#req.prod>. This now only contains the
|
||||
* configuration used by the former "MPS" product, which is now the only
|
||||
* product.
|
||||
*/
|
||||
|
||||
#define MPS_PROD_STRING "mps"
|
||||
#define MPS_PROD_MPS
|
||||
|
||||
|
||||
/* Default chain for GC pools
|
||||
*
|
||||
* TODO: The default should be to measure liveness and make sensible
|
||||
* decisions. See job003794.
|
||||
*/
|
||||
|
||||
#define ChainDEFAULT \
|
||||
{ \
|
||||
{ 8 * 1024, 0.85 }, /* nursery */ \
|
||||
{ 36 * 1024, 0.45 } /* second gen, after which dynamic */ \
|
||||
}
|
||||
|
||||
|
||||
/* Write barrier deferral
|
||||
*
|
||||
* <design/write-barrier#.deferral>.
|
||||
*
|
||||
* TODO: These settings were determined by trial and error, but should
|
||||
* be based on measurement of the protection overhead on each
|
||||
* platform. We know it's extremely different between macOS and
|
||||
* Windows, for example. <design/write-barrier#.improv.by-os>.
|
||||
*
|
||||
* TODO: Consider basing the count on the amount of time that has
|
||||
* passed in the mutator rather than the number of scans.
|
||||
*/
|
||||
|
||||
#define WB_DEFER_BITS 2 /* bitfield width for deferral count */
|
||||
#define WB_DEFER_INIT 3 /* boring scans after new segment */
|
||||
#define WB_DEFER_DELAY 3 /* boring scans after interesting scan */
|
||||
#define WB_DEFER_HIT 1 /* boring scans after barrier hit */
|
||||
|
||||
|
||||
/* Apple Hardened Runtime
|
||||
*
|
||||
* The MAYBE_HARDENED_RUNTIME macro is true if Apple's "Hardened
|
||||
* Runtime" feature may be enabled, and so calls to mmap() and
|
||||
* mprotect() with PROT_WRITE | PROT_EXEC may fail with EACCES.
|
||||
* See <design/prot#impl.xc.prot.exec> for details.
|
||||
*/
|
||||
#if defined(MPS_OS_XC) && defined(MPS_ARCH_A6)
|
||||
#define MAYBE_HARDENED_RUNTIME 1
|
||||
#else
|
||||
#define MAYBE_HARDENED_RUNTIME 0
|
||||
#endif
|
||||
|
||||
#endif /* config_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
813
mps/code/dbgpool.c
Normal file
813
mps/code/dbgpool.c
Normal file
|
|
@ -0,0 +1,813 @@
|
|||
/* dbgpool.c: POOL DEBUG MIXIN
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*
|
||||
* .source: <design/object-debug>
|
||||
*/
|
||||
|
||||
#include "dbgpool.h"
|
||||
#include "poolmfs.h"
|
||||
#include "splay.h"
|
||||
#include "mpm.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
SRCID(dbgpool, "$Id$");
|
||||
|
||||
|
||||
/* tagStruct -- tags for storing info about allocated objects */
|
||||
|
||||
typedef struct tagStruct {
|
||||
/* We don't want to pay the expense of a sig in every tag */
|
||||
Addr addr;
|
||||
Size size;
|
||||
TreeStruct treeStruct;
|
||||
char userdata[1 /* actually variable length */];
|
||||
} tagStruct;
|
||||
|
||||
#define TagTree(tag) (&(tag)->treeStruct)
|
||||
#define TagOfTree(tree) TREE_ELT(tag, treeStruct, tree)
|
||||
|
||||
typedef tagStruct *Tag;
|
||||
|
||||
|
||||
/* tag init methods: copying the user-supplied data into the tag */
|
||||
|
||||
static void TagTrivInit(void* tag, va_list args)
|
||||
{
|
||||
UNUSED(tag); UNUSED(args);
|
||||
}
|
||||
|
||||
|
||||
/* TagComp -- splay comparison function for address ordering of tags */
|
||||
|
||||
static Compare TagCompare(Tree node, TreeKey key)
|
||||
{
|
||||
Addr addr1, addr2;
|
||||
|
||||
addr1 = *(Addr *)key;
|
||||
addr2 = TagOfTree(node)->addr;
|
||||
if (addr1 < addr2)
|
||||
return CompareLESS;
|
||||
else if (addr1 > addr2) {
|
||||
/* Check key is not inside the object of this tag */
|
||||
AVER_CRITICAL(AddrAdd(addr2, TagOfTree(node)->size) <= addr1);
|
||||
return CompareGREATER;
|
||||
} else
|
||||
return CompareEQUAL;
|
||||
}
|
||||
|
||||
static TreeKey TagKey(Tree node)
|
||||
{
|
||||
return &TagOfTree(node)->addr;
|
||||
}
|
||||
|
||||
|
||||
/* PoolDebugMixinCheck -- check a PoolDebugMixin */
|
||||
|
||||
Bool PoolDebugMixinCheck(PoolDebugMixin debug)
|
||||
{
|
||||
/* Nothing to check about fenceTemplate */
|
||||
/* Nothing to check about fenceSize */
|
||||
/* Nothing to check about freeTemplate */
|
||||
/* Nothing to check about freeSize */
|
||||
if (debug->tagInit != NULL) {
|
||||
CHECKL(FUNCHECK(debug->tagInit));
|
||||
/* Nothing to check about tagSize */
|
||||
CHECKD(Pool, debug->tagPool);
|
||||
CHECKL(COMPATTYPE(Addr, void*)); /* tagPool relies on this */
|
||||
/* Nothing to check about missingTags */
|
||||
CHECKD(SplayTree, &debug->index);
|
||||
}
|
||||
UNUSED(debug); /* see <code/mpm.c#check.unused> */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolDebugMixin -- gets the debug mixin, if any */
|
||||
|
||||
#define DebugPoolDebugMixin(pool) (Method(Pool, pool, debugMixin)(pool))
|
||||
|
||||
|
||||
/* PoolNoDebugMixin -- debug mixin methods for pools with no mixin */
|
||||
|
||||
PoolDebugMixin PoolNoDebugMixin(Pool pool)
|
||||
{
|
||||
AVERT(Pool, pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* PoolDebugOptionsCheck -- check a PoolDebugOptions */
|
||||
|
||||
Bool PoolDebugOptionsCheck(PoolDebugOptions opt)
|
||||
{
|
||||
CHECKL(opt != NULL);
|
||||
if (opt->fenceSize != 0) {
|
||||
CHECKL(opt->fenceTemplate != NULL);
|
||||
/* Nothing to check about fenceSize */
|
||||
}
|
||||
if (opt->freeSize != 0) {
|
||||
CHECKL(opt->freeTemplate != NULL);
|
||||
/* Nothing to check about freeSize */
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolInit -- init method for a debug pool
|
||||
*
|
||||
* Someday, this could be split into fence and tag init methods.
|
||||
*/
|
||||
|
||||
ARG_DEFINE_KEY(POOL_DEBUG_OPTIONS, PoolDebugOptions);
|
||||
|
||||
static PoolDebugOptionsStruct debugPoolOptionsDefault = {
|
||||
"POST", 4, "DEAD", 4,
|
||||
};
|
||||
|
||||
static Res DebugPoolInit(Pool pool, Arena arena, PoolClass klass, ArgList args)
|
||||
{
|
||||
Res res;
|
||||
PoolDebugOptions options = &debugPoolOptionsDefault;
|
||||
PoolDebugMixin debug;
|
||||
TagInitFunction tagInit;
|
||||
Size tagSize;
|
||||
ArgStruct arg;
|
||||
|
||||
AVER(pool != NULL);
|
||||
AVERT(Arena, arena);
|
||||
AVERT(PoolClass, klass);
|
||||
AVERT(ArgList, args);
|
||||
|
||||
if (ArgPick(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS))
|
||||
options = (PoolDebugOptions)arg.val.pool_debug_options;
|
||||
|
||||
AVERT(PoolDebugOptions, options);
|
||||
|
||||
/* @@@@ Tag parameters should be taken from options, but tags have */
|
||||
/* not been published yet. */
|
||||
tagInit = NULL; tagSize = 0;
|
||||
|
||||
res = SuperclassPoly(Pool, klass)->init(pool, arena, klass, args);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
SetClassOfPoly(pool, klass);
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
AVER(debug != NULL);
|
||||
|
||||
/* fencepost init */
|
||||
/* @@@@ This parses a user argument, options, so it should really */
|
||||
/* go through the MPS interface. The template needs to be copied */
|
||||
/* into Addr memory, to avoid breaking <design/type#.addr.use>. */
|
||||
debug->fenceSize = options->fenceSize;
|
||||
if (debug->fenceSize != 0) {
|
||||
/* Fenceposting turns on tagging */
|
||||
if (tagInit == NULL) {
|
||||
tagSize = 0;
|
||||
tagInit = TagTrivInit;
|
||||
}
|
||||
debug->fenceTemplate = options->fenceTemplate;
|
||||
}
|
||||
|
||||
/* free-checking init */
|
||||
/* @@@@ This parses a user argument, options, so it should really */
|
||||
/* go through the MPS interface. The template needs to be copied */
|
||||
/* into Addr memory, to avoid breaking <design/type#addr.use>. */
|
||||
debug->freeSize = options->freeSize;
|
||||
if (debug->freeSize != 0) {
|
||||
debug->freeTemplate = options->freeTemplate;
|
||||
}
|
||||
|
||||
/* tag init */
|
||||
debug->tagInit = tagInit;
|
||||
if (debug->tagInit != NULL) {
|
||||
debug->tagSize = tagSize + sizeof(tagStruct) - 1;
|
||||
/* This pool has to be like the arena control pool: the blocks */
|
||||
/* allocated must be accessible using void*. */
|
||||
MPS_ARGS_BEGIN(pcArgs) {
|
||||
/* By setting EXTEND_BY to debug->tagSize we get the smallest
|
||||
possible extensions compatible with the tags, and so the
|
||||
least amount of wasted space. */
|
||||
MPS_ARGS_ADD(pcArgs, MPS_KEY_EXTEND_BY, debug->tagSize);
|
||||
MPS_ARGS_ADD(pcArgs, MPS_KEY_MFS_UNIT_SIZE, debug->tagSize);
|
||||
res = PoolCreate(&debug->tagPool, PoolArena(pool), PoolClassMFS(), pcArgs);
|
||||
} MPS_ARGS_END(pcArgs);
|
||||
if (res != ResOK)
|
||||
goto tagFail;
|
||||
debug->missingTags = 0;
|
||||
SplayTreeInit(&debug->index, TagCompare, TagKey, SplayTrivUpdate);
|
||||
}
|
||||
|
||||
debug->sig = PoolDebugMixinSig;
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
return ResOK;
|
||||
|
||||
tagFail:
|
||||
SuperclassPoly(Inst, klass)->finish(MustBeA(Inst, pool));
|
||||
AVER(res != ResOK);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolFinish -- finish method for a debug pool */
|
||||
|
||||
static void DebugPoolFinish(Inst inst)
|
||||
{
|
||||
Pool pool = MustBeA(AbstractPool, inst);
|
||||
PoolDebugMixin debug;
|
||||
PoolClass klass;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
AVER(debug != NULL);
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
if (debug->tagInit != NULL) {
|
||||
SplayTreeFinish(&debug->index);
|
||||
PoolDestroy(debug->tagPool);
|
||||
}
|
||||
klass = ClassOfPoly(Pool, pool);
|
||||
SuperclassPoly(Inst, klass)->finish(inst);
|
||||
}
|
||||
|
||||
|
||||
/* patternIterate -- call visitor for occurrences of pattern between
|
||||
* base and limit
|
||||
*
|
||||
* pattern is an arbitrary pattern that's size bytes long.
|
||||
*
|
||||
* Imagine that the entirety of memory were covered by contiguous
|
||||
* copies of pattern starting at address 0. Then call visitor for each
|
||||
* copy (or part) of pattern that lies between base and limit. In each
|
||||
* call, target is the address of the copy or part (where base <=
|
||||
* target < limit); source is the corresponding byte of the pattern
|
||||
* (where pattern <= source < pattern + size); and size is the length
|
||||
* of the copy or part.
|
||||
*/
|
||||
|
||||
typedef Bool (*patternVisitor)(Addr target, ReadonlyAddr source, Size size);
|
||||
|
||||
static Bool patternIterate(ReadonlyAddr pattern, Size size,
|
||||
Addr base, Addr limit, patternVisitor visitor)
|
||||
{
|
||||
Addr p;
|
||||
|
||||
AVER(pattern != NULL);
|
||||
AVER(0 < size);
|
||||
AVER(base != NULL);
|
||||
AVER(base <= limit);
|
||||
|
||||
p = base;
|
||||
while (p < limit) {
|
||||
Addr end = AddrAdd(p, size);
|
||||
Addr rounded = AddrRoundUp(p, size);
|
||||
Size offset = (Word)p % size;
|
||||
if (end < p || rounded < p) {
|
||||
/* Address range overflow */
|
||||
break;
|
||||
} else if (p == rounded && end <= limit) {
|
||||
/* Room for a whole copy */
|
||||
if (!(*visitor)(p, pattern, size))
|
||||
return FALSE;
|
||||
p = end;
|
||||
} else if (p < rounded && rounded <= end && rounded <= limit) {
|
||||
/* Copy up to rounded */
|
||||
if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset),
|
||||
AddrOffset(p, rounded)))
|
||||
return FALSE;
|
||||
p = rounded;
|
||||
} else {
|
||||
/* Copy up to limit */
|
||||
AVER(limit <= end);
|
||||
AVER(p == rounded || limit <= rounded);
|
||||
if (!(*visitor)(p, ReadonlyAddrAdd(pattern, offset),
|
||||
AddrOffset(p, limit)))
|
||||
return FALSE;
|
||||
p = limit;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* patternCopy -- copy pattern to fill a range
|
||||
*
|
||||
* Fill the range of addresses from base (inclusive) to limit
|
||||
* (exclusive) with copies of pattern (which is size bytes long).
|
||||
*/
|
||||
|
||||
static Bool patternCopyVisitor(Addr target, ReadonlyAddr source, Size size)
|
||||
{
|
||||
(void)AddrCopy(target, source, size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void patternCopy(ReadonlyAddr pattern, Size size, Addr base, Addr limit)
|
||||
{
|
||||
(void)patternIterate(pattern, size, base, limit, patternCopyVisitor);
|
||||
}
|
||||
|
||||
|
||||
/* patternCheck -- check pattern against a range
|
||||
*
|
||||
* Compare the range of addresses from base (inclusive) to limit
|
||||
* (exclusive) with copies of pattern (which is size bytes long). The
|
||||
* copies of pattern must be arranged so that fresh copies start at
|
||||
* aligned addresses wherever possible.
|
||||
*/
|
||||
|
||||
static Bool patternCheckVisitor(Addr target, ReadonlyAddr source, Size size)
|
||||
{
|
||||
return AddrComp(target, source, size) == 0;
|
||||
}
|
||||
|
||||
static Bool patternCheck(ReadonlyAddr pattern, Size size, Addr base, Addr limit)
|
||||
{
|
||||
return patternIterate(pattern, size, base, limit, patternCheckVisitor);
|
||||
}
|
||||
|
||||
|
||||
/* debugPoolSegIterate -- iterate over a range of segments in an arena
|
||||
*
|
||||
* Expects to be called on a range corresponding to objects within a
|
||||
* single pool.
|
||||
*
|
||||
* NOTE: This relies on pools consistently using segments
|
||||
* contiguously.
|
||||
*/
|
||||
|
||||
static void debugPoolSegIterate(Arena arena, Addr base, Addr limit,
|
||||
void (*visitor)(Arena, Seg))
|
||||
{
|
||||
Seg seg;
|
||||
|
||||
if (SegOfAddr(&seg, arena, base)) {
|
||||
do {
|
||||
base = SegLimit(seg);
|
||||
(*visitor)(arena, seg);
|
||||
} while (base < limit && SegOfAddr(&seg, arena, base));
|
||||
AVER(base >= limit); /* shouldn't run out of segments */
|
||||
}
|
||||
}
|
||||
|
||||
static void debugPoolShieldExpose(Arena arena, Seg seg)
|
||||
{
|
||||
ShieldExpose(arena, seg);
|
||||
}
|
||||
|
||||
static void debugPoolShieldCover(Arena arena, Seg seg)
|
||||
{
|
||||
ShieldCover(arena, seg);
|
||||
}
|
||||
|
||||
|
||||
/* freeSplat -- splat free block with splat pattern */
|
||||
|
||||
static void freeSplat(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
|
||||
{
|
||||
Arena arena;
|
||||
|
||||
AVER(base < limit);
|
||||
|
||||
/* If the block is in one or more segments, make sure the segments
|
||||
are exposed so that we can overwrite the block with the pattern. */
|
||||
arena = PoolArena(pool);
|
||||
debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose);
|
||||
patternCopy(debug->freeTemplate, debug->freeSize, base, limit);
|
||||
debugPoolSegIterate(arena, base, limit, debugPoolShieldCover);
|
||||
}
|
||||
|
||||
|
||||
/* freeCheck -- check free block for splat pattern */
|
||||
|
||||
static Bool freeCheck(PoolDebugMixin debug, Pool pool, Addr base, Addr limit)
|
||||
{
|
||||
Bool res;
|
||||
Arena arena;
|
||||
|
||||
AVER(base < limit);
|
||||
|
||||
/* If the block is in one or more segments, make sure the segments
|
||||
are exposed so we can read the pattern. */
|
||||
arena = PoolArena(pool);
|
||||
debugPoolSegIterate(arena, base, limit, debugPoolShieldExpose);
|
||||
res = patternCheck(debug->freeTemplate, debug->freeSize, base, limit);
|
||||
debugPoolSegIterate(arena, base, limit, debugPoolShieldCover);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* freeCheckAlloc -- allocation wrapper for free-checking */
|
||||
|
||||
static Res freeCheckAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool,
|
||||
Size size)
|
||||
{
|
||||
Res res;
|
||||
Addr new;
|
||||
PoolClass klass;
|
||||
|
||||
AVER(aReturn != NULL);
|
||||
|
||||
klass = ClassOfPoly(Pool, pool);
|
||||
res = SuperclassPoly(Pool, klass)->alloc(&new, pool, size);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
if (debug->freeSize != 0)
|
||||
ASSERT(freeCheck(debug, pool, new, AddrAdd(new, size)),
|
||||
"free space corrupted on alloc");
|
||||
|
||||
*aReturn = new;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* freeCheckFree -- freeing wrapper for free-checking */
|
||||
|
||||
static void freeCheckFree(PoolDebugMixin debug,
|
||||
Pool pool, Addr old, Size size)
|
||||
{
|
||||
PoolClass klass;
|
||||
if (debug->freeSize != 0)
|
||||
freeSplat(debug, pool, old, AddrAdd(old, size));
|
||||
klass = ClassOfPoly(Pool, pool);
|
||||
SuperclassPoly(Pool, klass)->free(pool, old, size);
|
||||
}
|
||||
|
||||
|
||||
/* fenceAlloc -- allocation wrapper for fenceposts
|
||||
*
|
||||
* Allocates an object, adding fenceposts on both sides. Layout:
|
||||
*
|
||||
* |----------|-------------------------------------|------|----------|
|
||||
* start fp client object slop end fp
|
||||
*
|
||||
* slop is the extra allocation from rounding up the client request to
|
||||
* the pool's alignment. The fenceposting code adds this slop so that
|
||||
* there's a better chance of the end fencepost being flush with the
|
||||
* next object (though it can't be guaranteed, since the underlying
|
||||
* pool could have allocated an even larger block). The alignment slop
|
||||
* is filled from the fencepost template as well.
|
||||
*
|
||||
* Keep in sync with fenceCheck.
|
||||
*/
|
||||
|
||||
static Res fenceAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool,
|
||||
Size size)
|
||||
{
|
||||
Res res;
|
||||
Addr obj, startFence, clientNew, clientLimit, limit;
|
||||
Size alignedFenceSize, alignedSize;
|
||||
|
||||
AVER(aReturn != NULL);
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
AVERT(Pool, pool);
|
||||
|
||||
alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
|
||||
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
|
||||
res = freeCheckAlloc(&obj, debug, pool,
|
||||
alignedSize + 2 * alignedFenceSize);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
startFence = obj;
|
||||
clientNew = AddrAdd(startFence, alignedFenceSize);
|
||||
clientLimit = AddrAdd(clientNew, size);
|
||||
limit = AddrAdd(clientNew, alignedSize + alignedFenceSize);
|
||||
|
||||
/* @@@@ shields? */
|
||||
patternCopy(debug->fenceTemplate, debug->fenceSize, startFence, clientNew);
|
||||
patternCopy(debug->fenceTemplate, debug->fenceSize, clientLimit, limit);
|
||||
|
||||
*aReturn = clientNew;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* fenceCheck -- check fences of an object
|
||||
*
|
||||
* Keep in sync with fenceAlloc.
|
||||
*/
|
||||
|
||||
static Bool fenceCheck(PoolDebugMixin debug, Pool pool, Addr obj, Size size)
|
||||
{
|
||||
Addr startFence, clientNew, clientLimit, limit;
|
||||
Size alignedFenceSize, alignedSize;
|
||||
|
||||
AVERT_CRITICAL(PoolDebugMixin, debug);
|
||||
AVERT_CRITICAL(Pool, pool);
|
||||
/* Can't check obj */
|
||||
|
||||
alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
|
||||
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
|
||||
|
||||
startFence = AddrSub(obj, alignedFenceSize);
|
||||
clientNew = obj;
|
||||
clientLimit = AddrAdd(clientNew, size);
|
||||
limit = AddrAdd(clientNew, alignedSize + alignedFenceSize);
|
||||
|
||||
/* @@@@ shields? */
|
||||
return patternCheck(debug->fenceTemplate, debug->fenceSize,
|
||||
startFence, clientNew)
|
||||
&& patternCheck(debug->fenceTemplate, debug->fenceSize,
|
||||
clientLimit, limit);
|
||||
}
|
||||
|
||||
|
||||
/* fenceFree -- freeing wrapper for fenceposts */
|
||||
|
||||
static void fenceFree(PoolDebugMixin debug,
|
||||
Pool pool, Addr old, Size size)
|
||||
{
|
||||
Size alignedFenceSize, alignedSize;
|
||||
|
||||
ASSERT(fenceCheck(debug, pool, old, size), "fencepost check on free"); /* <design/check/#.common> */
|
||||
|
||||
alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool));
|
||||
alignedSize = SizeAlignUp(size, PoolAlignment(pool));
|
||||
freeCheckFree(debug, pool, AddrSub(old, alignedFenceSize),
|
||||
alignedSize + 2 * alignedFenceSize);
|
||||
}
|
||||
|
||||
|
||||
/* tagAlloc -- allocation wrapper for tagged pools */
|
||||
|
||||
static Res tagAlloc(PoolDebugMixin debug,
|
||||
Pool pool, Addr new, Size size)
|
||||
{
|
||||
Tag tag;
|
||||
Res res;
|
||||
Bool b;
|
||||
Addr addr;
|
||||
|
||||
UNUSED(pool);
|
||||
res = PoolAlloc(&addr, debug->tagPool, debug->tagSize);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
tag = (Tag)addr;
|
||||
tag->addr = new; tag->size = size;
|
||||
TreeInit(TagTree(tag));
|
||||
/* In the future, we might call debug->tagInit here. */
|
||||
b = SplayTreeInsert(&debug->index, TagTree(tag));
|
||||
AVER(b);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* tagFree -- deallocation wrapper for tagged pools */
|
||||
|
||||
static void tagFree(PoolDebugMixin debug, Pool pool, Addr old, Size size)
|
||||
{
|
||||
Tree node;
|
||||
Tag tag;
|
||||
Bool b;
|
||||
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
AVERT(Pool, pool);
|
||||
AVER(size > 0);
|
||||
|
||||
if (!SplayTreeFind(&node, &debug->index, &old)) {
|
||||
AVER(debug->missingTags > 0);
|
||||
debug->missingTags--;
|
||||
return;
|
||||
}
|
||||
tag = TagOfTree(node);
|
||||
AVER(tag->size == size);
|
||||
AVER(tag->addr == old);
|
||||
b = SplayTreeDelete(&debug->index, node);
|
||||
AVER(b); /* expect tag to be in the tree */
|
||||
TreeFinish(node);
|
||||
PoolFree(debug->tagPool, (Addr)tag, debug->tagSize);
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolAlloc -- alloc method for a debug pool
|
||||
*
|
||||
* Eventually, tag init args will need to be handled somewhere here.
|
||||
*/
|
||||
|
||||
static Res DebugPoolAlloc(Addr *aReturn, Pool pool, Size size)
|
||||
{
|
||||
Res res;
|
||||
Addr new = NULL; /* suppress "may be used uninitialized" warning */
|
||||
PoolDebugMixin debug;
|
||||
|
||||
AVER(aReturn != NULL);
|
||||
AVERT(Pool, pool);
|
||||
AVER(size > 0);
|
||||
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
AVER(debug != NULL);
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
if (debug->fenceSize != 0)
|
||||
res = fenceAlloc(&new, debug, pool, size);
|
||||
else
|
||||
res = freeCheckAlloc(&new, debug, pool, size);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
/* Allocate object first, so it fits even when the tag doesn't. */
|
||||
if (debug->tagInit != NULL) {
|
||||
res = tagAlloc(debug, pool, new, size);
|
||||
if (res != ResOK)
|
||||
goto tagFail;
|
||||
}
|
||||
|
||||
*aReturn = new;
|
||||
return res;
|
||||
|
||||
tagFail:
|
||||
fenceFree(debug, pool, new, size);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolFree -- free method for a debug pool */
|
||||
|
||||
static void DebugPoolFree(Pool pool, Addr old, Size size)
|
||||
{
|
||||
PoolDebugMixin debug;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
/* Can't check old */
|
||||
AVER(size > 0);
|
||||
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
AVER(debug != NULL);
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
|
||||
if (debug->fenceSize != 0)
|
||||
fenceFree(debug, pool, old, size);
|
||||
else
|
||||
freeCheckFree(debug, pool, old, size);
|
||||
/* Free the object first, to get fences checked before tag. */
|
||||
if (debug->tagInit != NULL)
|
||||
tagFree(debug, pool, old, size);
|
||||
}
|
||||
|
||||
|
||||
/* TagWalk -- walk all objects in the pool using tags */
|
||||
|
||||
typedef void (*ObjectsVisitor)(Addr addr, Size size, Format fmt,
|
||||
Pool pool, void *tagData, void *p);
|
||||
|
||||
static void TagWalk(Pool pool, ObjectsVisitor visitor, void *p)
|
||||
{
|
||||
Tree node;
|
||||
PoolDebugMixin debug;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
AVER(FUNCHECK(visitor));
|
||||
/* Can't check p */
|
||||
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
AVER(debug != NULL);
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
|
||||
node = SplayTreeFirst(&debug->index);
|
||||
while (node != TreeEMPTY) {
|
||||
Tag tag = TagOfTree(node);
|
||||
|
||||
(*visitor)(tag->addr, tag->size, NULL, pool, &tag->userdata, p);
|
||||
node = SplayTreeNext(&debug->index, &tag->addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* fenceCheckingStep -- step function for DebugPoolCheckFences */
|
||||
|
||||
static void fenceCheckingStep(Addr addr, Size size, Format fmt,
|
||||
Pool pool, void *tagData, void *p)
|
||||
{
|
||||
/* no need to check arguments checked in the caller */
|
||||
UNUSED(fmt); UNUSED(tagData);
|
||||
ASSERT(fenceCheck((PoolDebugMixin)p, pool, addr, size),
|
||||
"fencepost check requested by client");
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolCheckFences -- check all the fenceposts in the pool */
|
||||
|
||||
void DebugPoolCheckFences(Pool pool)
|
||||
{
|
||||
PoolDebugMixin debug;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
if (debug == NULL)
|
||||
return;
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
|
||||
if (debug->fenceSize != 0)
|
||||
TagWalk(pool, fenceCheckingStep, (void *)debug);
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolFreeSplat -- if in a free-checking debug pool, splat free block */
|
||||
|
||||
void DebugPoolFreeSplat(Pool pool, Addr base, Addr limit)
|
||||
{
|
||||
PoolDebugMixin debug;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
AVER(PoolHasAddr(pool, base));
|
||||
AVER(PoolHasAddr(pool, AddrSub(limit, 1)));
|
||||
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
if (debug != NULL) {
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
if (debug->freeSize != 0)
|
||||
freeSplat(debug, pool, base, limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolFreeCheck -- if in a free-checking debug pool, check free block */
|
||||
|
||||
void DebugPoolFreeCheck(Pool pool, Addr base, Addr limit)
|
||||
{
|
||||
PoolDebugMixin debug;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
AVER(PoolHasAddr(pool, base));
|
||||
AVER(PoolHasAddr(pool, AddrSub(limit, 1)));
|
||||
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
if (debug != NULL) {
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
if (debug->freeSize != 0)
|
||||
ASSERT(freeCheck(debug, pool, base, limit),
|
||||
"free space corrupted on release"); /* <design/check/#.common> */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* freeCheckingStep -- step function for DebugPoolCheckFreeSpace */
|
||||
|
||||
static void freeCheckingStep(Addr base, Addr limit, Pool pool, void *p)
|
||||
{
|
||||
/* no need to check arguments checked in the caller */
|
||||
ASSERT(freeCheck((PoolDebugMixin)p, pool, base, limit),
|
||||
"free space corrupted on client check");
|
||||
}
|
||||
|
||||
|
||||
/* DebugPoolCheckFreeSpace -- check free space in the pool for overwrites */
|
||||
|
||||
void DebugPoolCheckFreeSpace(Pool pool)
|
||||
{
|
||||
PoolDebugMixin debug;
|
||||
|
||||
AVERT(Pool, pool);
|
||||
debug = DebugPoolDebugMixin(pool);
|
||||
if (debug == NULL)
|
||||
return;
|
||||
AVERT(PoolDebugMixin, debug);
|
||||
|
||||
if (debug->freeSize != 0)
|
||||
PoolFreeWalk(pool, freeCheckingStep, (void *)debug);
|
||||
}
|
||||
|
||||
|
||||
/* PoolClassMixInDebug -- mix in the debug support for class init */
|
||||
|
||||
void PoolClassMixInDebug(PoolClass klass)
|
||||
{
|
||||
/* Can't check klass because it's not initialized yet */
|
||||
klass->instClassStruct.finish = DebugPoolFinish;
|
||||
klass->init = DebugPoolInit;
|
||||
klass->alloc = DebugPoolAlloc;
|
||||
klass->free = DebugPoolFree;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
101
mps/code/dbgpool.h
Normal file
101
mps/code/dbgpool.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/* dbgpool.h: POOL DEBUG MIXIN
|
||||
*
|
||||
* <design/object-debug>.
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*/
|
||||
|
||||
#ifndef dbgpool_h
|
||||
#define dbgpool_h
|
||||
|
||||
#include "splay.h"
|
||||
#include "mpmtypes.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
/* tag init function: copies the user-supplied data into the tag */
|
||||
|
||||
typedef void (*TagInitFunction)(void *tag, va_list args);
|
||||
|
||||
|
||||
/* PoolDebugOptions -- option structure for debug pool init
|
||||
*
|
||||
* This must be kept in sync with <code/mps.h#mps_pool_debug_option_s>.
|
||||
*/
|
||||
|
||||
typedef struct PoolDebugOptionsStruct {
|
||||
const void *fenceTemplate;
|
||||
Size fenceSize;
|
||||
const void *freeTemplate;
|
||||
Size freeSize;
|
||||
/* TagInitFunction tagInit; */
|
||||
/* Size tagSize; */
|
||||
} PoolDebugOptionsStruct;
|
||||
|
||||
typedef PoolDebugOptionsStruct *PoolDebugOptions;
|
||||
|
||||
|
||||
/* PoolDebugMixinStruct -- internal structure for debug mixins */
|
||||
|
||||
#define PoolDebugMixinSig ((Sig)0x519B0DB9) /* SIGnature POol DeBuG */
|
||||
|
||||
typedef struct PoolDebugMixinStruct {
|
||||
Sig sig; /* design.mps.sig.field */
|
||||
const struct AddrStruct *fenceTemplate;
|
||||
Size fenceSize;
|
||||
const struct AddrStruct *freeTemplate;
|
||||
Size freeSize;
|
||||
TagInitFunction tagInit;
|
||||
Size tagSize;
|
||||
Pool tagPool;
|
||||
Count missingTags;
|
||||
SplayTreeStruct index;
|
||||
} PoolDebugMixinStruct;
|
||||
|
||||
|
||||
extern Bool PoolDebugOptionsCheck(PoolDebugOptions opt);
|
||||
|
||||
extern Bool PoolDebugMixinCheck(PoolDebugMixin dbg);
|
||||
|
||||
extern void PoolClassMixInDebug(PoolClass klass);
|
||||
|
||||
extern void DebugPoolCheckFences(Pool pool);
|
||||
extern void DebugPoolCheckFreeSpace(Pool pool);
|
||||
|
||||
extern void DebugPoolFreeSplat(Pool pool, Addr base, Addr limit);
|
||||
extern void DebugPoolFreeCheck(Pool pool, Addr base, Addr limit);
|
||||
|
||||
|
||||
#endif /* dbgpool_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
84
mps/code/dbgpooli.c
Normal file
84
mps/code/dbgpooli.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* dbgpooli.c: POOL DEBUG MIXIN C INTERFACE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*
|
||||
* .source: <design/object-debug>
|
||||
*/
|
||||
|
||||
#include "dbgpool.h"
|
||||
#include "mps.h"
|
||||
#include "mpm.h"
|
||||
|
||||
SRCID(dbgpooli, "$Id$");
|
||||
|
||||
|
||||
/* mps_pool_check_fenceposts -- check all the fenceposts in the pool */
|
||||
|
||||
void mps_pool_check_fenceposts(mps_pool_t mps_pool)
|
||||
{
|
||||
Pool pool = (Pool)mps_pool;
|
||||
Arena arena;
|
||||
|
||||
/* TESTT not AVERT, see <design/interface-c#.check.space */
|
||||
AVER(TESTT(Pool, pool));
|
||||
arena = PoolArena(pool);
|
||||
|
||||
ArenaEnter(arena);
|
||||
|
||||
AVERT(Pool, pool);
|
||||
DebugPoolCheckFences(pool);
|
||||
|
||||
ArenaLeave(arena);
|
||||
}
|
||||
|
||||
|
||||
/* mps_pool_check_free_space -- check free space in the pool for overwrites */
|
||||
|
||||
void mps_pool_check_free_space(mps_pool_t mps_pool)
|
||||
{
|
||||
Pool pool = (Pool)mps_pool;
|
||||
Arena arena;
|
||||
|
||||
/* TESTT not AVERT, see <design/interface-c#.check.space */
|
||||
AVER(TESTT(Pool, pool));
|
||||
arena = PoolArena(pool);
|
||||
|
||||
ArenaEnter(arena);
|
||||
|
||||
AVERT(Pool, pool);
|
||||
DebugPoolCheckFreeSpace(pool);
|
||||
|
||||
ArenaLeave(arena);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
423
mps/code/djbench.c
Normal file
423
mps/code/djbench.c
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
/* djbench.c -- "DJ" Benchmark on ANSI C library
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2013-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* This is an allocation stress benchmark test for manual variable pools
|
||||
* and also for stdlib malloc/free (for comparison).
|
||||
*
|
||||
* It repeatedly runs over an array of blocks and allocates or frees them
|
||||
* with some probability, then frees all the remaining blocks at the end.
|
||||
* This test can be iterated.
|
||||
*/
|
||||
|
||||
#include "mps.c"
|
||||
|
||||
#include "testlib.h"
|
||||
#include "testthr.h"
|
||||
|
||||
#ifdef MPS_OS_W3
|
||||
#include "getopt.h"
|
||||
#else
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h> /* fprintf, stderr */
|
||||
#include <stdlib.h> /* alloca, exit, EXIT_SUCCESS, EXIT_FAILURE */
|
||||
#include <time.h> /* CLOCKS_PER_SEC, clock */
|
||||
|
||||
#define DJMUST(expr) \
|
||||
do { \
|
||||
mps_res_t res = (expr); \
|
||||
if (res != MPS_RES_OK) { \
|
||||
fprintf(stderr, #expr " returned %d\n", res); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static mps_arena_t arena;
|
||||
static mps_pool_t pool;
|
||||
|
||||
|
||||
/* The benchmark behaviour is defined as a macro in order to give realistic
|
||||
opportunities for compiler optimisation and the intended inlining of the
|
||||
MPS functions. */
|
||||
|
||||
static rnd_state_t seed = 0; /* random number seed */
|
||||
static unsigned nthreads = 1; /* threads */
|
||||
static unsigned niter = 50; /* iterations */
|
||||
static unsigned npass = 100; /* passes over blocks */
|
||||
static unsigned nblocks = 64; /* number of blocks */
|
||||
static unsigned sshift = 18; /* log2 max block size in words */
|
||||
static double pact = 0.2; /* probability per pass of acting */
|
||||
static unsigned rinter = 75; /* pass interval for recursion */
|
||||
static unsigned rmax = 10; /* maximum recursion depth */
|
||||
static mps_bool_t zoned = TRUE; /* arena allocates using zones */
|
||||
static size_t arena_size = 256ul * 1024 * 1024; /* arena size */
|
||||
static size_t arena_grain_size = 1; /* arena grain size */
|
||||
static double spare = ARENA_SPARE_DEFAULT; /* spare commit fraction */
|
||||
|
||||
#define DJRUN(fname, alloc, free) \
|
||||
static unsigned fname##_inner(mps_ap_t ap, unsigned depth, unsigned r) { \
|
||||
struct {void *p; size_t s;} *blocks = alloca(sizeof(blocks[0]) * nblocks); \
|
||||
unsigned j, k; \
|
||||
\
|
||||
for (k = 0; k < nblocks; ++k) { \
|
||||
blocks[k].p = NULL; \
|
||||
blocks[k].s = 0; \
|
||||
} \
|
||||
\
|
||||
for (j = 0; j < npass; ++j) { \
|
||||
for (k = 0; k < nblocks; ++k) { \
|
||||
if (rnd() % 16384 < pact * 16384) { \
|
||||
if (blocks[k].p == NULL) { \
|
||||
size_t s = rnd() % ((sizeof(void *) << (rnd() % sshift)) - 1); \
|
||||
void *p = NULL; \
|
||||
if (s > 0) \
|
||||
alloc(p, s); \
|
||||
blocks[k].p = p; \
|
||||
blocks[k].s = s; \
|
||||
} else { \
|
||||
free(blocks[k].p, blocks[k].s); \
|
||||
blocks[k].p = NULL; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (rinter > 0 && depth > 0 && ++r % rinter == 0) { \
|
||||
/* putchar('>'); fflush(stdout); */ \
|
||||
r = fname##_inner(ap, depth - 1, r); \
|
||||
/* putchar('<'); fflush(stdout); */ \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
for (k = 0; k < nblocks; ++k) { \
|
||||
if (blocks[k].p) { \
|
||||
free(blocks[k].p, blocks[k].s); \
|
||||
blocks[k].p = NULL; \
|
||||
} \
|
||||
} \
|
||||
return r; \
|
||||
} \
|
||||
\
|
||||
static void *fname(void *p) { \
|
||||
unsigned i; \
|
||||
mps_ap_t ap = NULL; \
|
||||
if (pool != NULL) \
|
||||
DJMUST(mps_ap_create_k(&ap, pool, mps_args_none)); \
|
||||
for (i = 0; i < niter; ++i) \
|
||||
(void)fname##_inner(ap, rmax, 0); \
|
||||
if (ap != NULL) \
|
||||
mps_ap_destroy(ap); \
|
||||
return p; \
|
||||
}
|
||||
|
||||
|
||||
/* malloc/free benchmark */
|
||||
|
||||
#define MALLOC_ALLOC(p, s) do { p = malloc(s); } while(0)
|
||||
#define MALLOC_FREE(p, s) do { free(p); } while(0)
|
||||
|
||||
DJRUN(dj_malloc, MALLOC_ALLOC, MALLOC_FREE)
|
||||
|
||||
|
||||
/* mps_alloc/mps_free benchmark */
|
||||
|
||||
#define MPS_ALLOC(p, s) do { mps_alloc(&p, pool, s); } while(0)
|
||||
#define MPS_FREE(p, s) do { mps_free(pool, p, s); } while(0)
|
||||
|
||||
DJRUN(dj_alloc, MPS_ALLOC, MPS_FREE)
|
||||
|
||||
|
||||
/* reserve/free benchmark */
|
||||
|
||||
#define ALIGN_UP(s, a) (((s) + ((a) - 1)) & ~((a) - 1))
|
||||
#define RESERVE_ALLOC(p, s) \
|
||||
do { \
|
||||
size_t _s = ALIGN_UP(s, (size_t)MPS_PF_ALIGN); \
|
||||
(void)mps_reserve(&p, ap, _s); \
|
||||
(void)mps_commit(ap, p, _s); \
|
||||
} while(0)
|
||||
#define RESERVE_FREE(p, s) do { mps_free(pool, p, s); } while(0)
|
||||
|
||||
DJRUN(dj_reserve, RESERVE_ALLOC, RESERVE_FREE)
|
||||
|
||||
typedef void *(*dj_t)(void *);
|
||||
|
||||
static void weave(dj_t dj)
|
||||
{
|
||||
testthr_t *threads = alloca(sizeof(threads[0]) * nthreads);
|
||||
unsigned t;
|
||||
|
||||
for (t = 0; t < nthreads; ++t)
|
||||
testthr_create(&threads[t], dj, NULL);
|
||||
|
||||
for (t = 0; t < nthreads; ++t)
|
||||
testthr_join(&threads[t], NULL);
|
||||
}
|
||||
|
||||
|
||||
static void watch(dj_t dj, const char *name)
|
||||
{
|
||||
clock_t start, finish;
|
||||
|
||||
start = clock();
|
||||
if (nthreads == 1)
|
||||
dj(NULL);
|
||||
else
|
||||
weave(dj);
|
||||
finish = clock();
|
||||
|
||||
printf("%s: %g\n", name, (double)(finish - start) / CLOCKS_PER_SEC);
|
||||
}
|
||||
|
||||
|
||||
/* Wrap a call to dj benchmark that doesn't require MPS setup */
|
||||
|
||||
static void wrap(dj_t dj, mps_pool_class_t dummy, const char *name)
|
||||
{
|
||||
(void)dummy;
|
||||
pool = NULL;
|
||||
watch(dj, name);
|
||||
}
|
||||
|
||||
|
||||
/* Wrap a call to a dj benchmark that requires MPS setup */
|
||||
|
||||
static void arena_wrap(dj_t dj, mps_pool_class_t pool_class, const char *name)
|
||||
{
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_SPARE, spare);
|
||||
DJMUST(mps_arena_create_k(&arena, mps_arena_class_vm(), args));
|
||||
} MPS_ARGS_END(args);
|
||||
DJMUST(mps_pool_create_k(&pool, arena, pool_class, mps_args_none));
|
||||
watch(dj, name);
|
||||
mps_pool_destroy(pool);
|
||||
mps_arena_destroy(arena);
|
||||
}
|
||||
|
||||
|
||||
/* Command-line options definitions. See getopt_long(3). */
|
||||
|
||||
static struct option longopts[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"nthreads", required_argument, NULL, 't'},
|
||||
{"niter", required_argument, NULL, 'i'},
|
||||
{"npass", required_argument, NULL, 'p'},
|
||||
{"nblocks", required_argument, NULL, 'b'},
|
||||
{"sshift", required_argument, NULL, 's'},
|
||||
{"pact", required_argument, NULL, 'c'},
|
||||
{"rinter", required_argument, NULL, 'r'},
|
||||
{"rmax", required_argument, NULL, 'd'},
|
||||
{"seed", required_argument, NULL, 'x'},
|
||||
{"arena-size", required_argument, NULL, 'm'},
|
||||
{"arena-grain-size", required_argument, NULL, 'a'},
|
||||
{"arena-unzoned", no_argument, NULL, 'z'},
|
||||
{"spare", required_argument, NULL, 'S'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
/* Test definitions. */
|
||||
|
||||
static mps_pool_class_t dummy_class(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
void (*wrap)(dj_t, mps_pool_class_t, const char *name);
|
||||
dj_t dj;
|
||||
mps_pool_class_t (*pool_class)(void);
|
||||
} pools[] = {
|
||||
{"mvt", arena_wrap, dj_reserve, mps_class_mvt},
|
||||
{"mvff", arena_wrap, dj_reserve, mps_class_mvff},
|
||||
{"mvffa", arena_wrap, dj_alloc, mps_class_mvff}, /* mvff with alloc */
|
||||
{"an", wrap, dj_malloc, dummy_class},
|
||||
};
|
||||
|
||||
|
||||
/* Command-line driver */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ch;
|
||||
unsigned i;
|
||||
mps_bool_t seed_specified = FALSE;
|
||||
|
||||
seed = rnd_seed();
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "ht:i:p:b:s:c:r:d:m:a:x:zS:",
|
||||
longopts, NULL)) != -1)
|
||||
switch (ch) {
|
||||
case 't':
|
||||
nthreads = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'i':
|
||||
niter = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'p':
|
||||
npass = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'b':
|
||||
nblocks = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 's':
|
||||
sshift = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'c':
|
||||
pact = strtod(optarg, NULL);
|
||||
break;
|
||||
case 'r':
|
||||
rinter = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'd':
|
||||
rmax = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'x':
|
||||
seed = strtoul(optarg, NULL, 10);
|
||||
seed_specified = TRUE;
|
||||
break;
|
||||
case 'z':
|
||||
zoned = FALSE;
|
||||
break;
|
||||
case 'm': {
|
||||
char *p;
|
||||
arena_size = (unsigned)strtoul(optarg, &p, 10);
|
||||
switch(toupper(*p)) {
|
||||
case 'G': arena_size <<= 30; break;
|
||||
case 'M': arena_size <<= 20; break;
|
||||
case 'K': arena_size <<= 10; break;
|
||||
case '\0': break;
|
||||
default:
|
||||
fprintf(stderr, "Bad arena size %s\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'a': {
|
||||
char *p;
|
||||
arena_grain_size = (unsigned)strtoul(optarg, &p, 10);
|
||||
switch(toupper(*p)) {
|
||||
case 'G': arena_grain_size <<= 30; break;
|
||||
case 'M': arena_grain_size <<= 20; break;
|
||||
case 'K': arena_grain_size <<= 10; break;
|
||||
case '\0': break;
|
||||
default:
|
||||
fprintf(stderr, "Bad arena grain size %s\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
spare = strtod(optarg, NULL);
|
||||
break;
|
||||
default:
|
||||
/* This is printed in parts to keep within the 509 character
|
||||
limit for string literals in portable standard C. */
|
||||
fprintf(stderr,
|
||||
"Usage: %s [option...] [test...]\n"
|
||||
"Options:\n"
|
||||
" -m n, --arena-size=n[KMG]?\n"
|
||||
" Initial size of arena (default %lu)\n"
|
||||
" -g n, --arena-grain-size=n[KMG]?\n"
|
||||
" Arena grain size (default %lu)\n"
|
||||
" -t n, --nthreads=n\n"
|
||||
" Launch n threads each running the test\n"
|
||||
" -i n, --niter=n\n"
|
||||
" Iterate each test n times (default %u)\n"
|
||||
" -p n, --npass=n\n"
|
||||
" Pass over the block array n times (default %u)\n"
|
||||
" -b n, --nblocks=n\n"
|
||||
" Length of the block array (default %u)\n"
|
||||
" -s n, --sshift=n\n"
|
||||
" Log2 max block size in words (default %u)\n",
|
||||
argv[0],
|
||||
(unsigned long)arena_size,
|
||||
(unsigned long)arena_grain_size,
|
||||
niter,
|
||||
npass,
|
||||
nblocks,
|
||||
sshift);
|
||||
fprintf(stderr,
|
||||
" -c p, --pact=p\n"
|
||||
" Probability of acting on a block (default %g)\n"
|
||||
" -r n, --rinter=n\n"
|
||||
" Recurse every n passes if n > 0 (default %u)\n"
|
||||
" -d n, --rmax=n\n"
|
||||
" Maximum recursion depth (default %u)\n"
|
||||
" -x n, --seed=n\n"
|
||||
" Random number seed (default from entropy)\n"
|
||||
" -z, --arena-unzoned\n"
|
||||
" Disabled zoned allocation in the arena\n"
|
||||
" -S f, --spare\n"
|
||||
" Maximum spare committed fraction (default %f)\n",
|
||||
pact,
|
||||
rinter,
|
||||
rmax,
|
||||
spare);
|
||||
fprintf(stderr,
|
||||
"Tests:\n"
|
||||
" mvt pool class MVT\n"
|
||||
" mvff pool class MVFF (buffer interface)\n"
|
||||
" mvffa pool class MVFF (alloc interface)\n"
|
||||
" an malloc\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (!seed_specified) {
|
||||
printf("seed: %lu\n", seed);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
while (argc > 0) {
|
||||
for (i = 0; i < NELEMS(pools); ++i)
|
||||
if (strcmp(argv[0], pools[i].name) == 0)
|
||||
goto found;
|
||||
fprintf(stderr, "unknown pool test \"%s\"\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
found:
|
||||
(void)mps_lib_assert_fail_install(assert_die);
|
||||
rnd_state_set(seed);
|
||||
pools[i].wrap(pools[i].dj, pools[i].pool_class(), pools[i].name);
|
||||
--argc;
|
||||
++argv;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2013-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
568
mps/code/event.c
Normal file
568
mps/code/event.c
Normal file
|
|
@ -0,0 +1,568 @@
|
|||
/* event.c: EVENT LOGGING
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .sources: mps.design.event
|
||||
*
|
||||
* TRANSGRESSIONS (rule.impl.trans)
|
||||
*
|
||||
* .trans.ifdef: This file should logically be split into two, event.c
|
||||
* (which contains NOOP definitions, for general use) and eventdl.c, which
|
||||
* is specific to the logging variety and actually does logging (maybe).
|
||||
* Unfortunately, the build system doesn't really cope, and so this file
|
||||
* consists of two versions which are conditional on the EVENT symbol.
|
||||
*/
|
||||
|
||||
#include "mpm.h"
|
||||
#include "event.h"
|
||||
#include "mpsio.h"
|
||||
#include "lock.h"
|
||||
|
||||
SRCID(event, "$Id$");
|
||||
|
||||
|
||||
#ifdef EVENT /* .trans.ifdef */
|
||||
|
||||
|
||||
static Bool eventInited = FALSE;
|
||||
static Bool eventIOInited = FALSE;
|
||||
static mps_io_t eventIO;
|
||||
static Serial EventInternSerial;
|
||||
|
||||
/* Buffers in which events are recorded, from the top down. */
|
||||
char EventBuffer[EventKindLIMIT][EventBufferSIZE];
|
||||
|
||||
/* Pointers to last event logged into each buffer. */
|
||||
char *EventLast[EventKindLIMIT];
|
||||
|
||||
/* Pointers to the last event written out of each buffer. */
|
||||
static char *EventWritten[EventKindLIMIT];
|
||||
|
||||
EventControlSet EventKindControl; /* Bit set used to control output. */
|
||||
|
||||
|
||||
/* A single event structure output once per buffer flush. */
|
||||
static EventEventClockSyncStruct eventClockSyncStruct;
|
||||
|
||||
|
||||
/* eventClockSync -- Populate and write the clock sync event. */
|
||||
|
||||
static Res eventClockSync(void)
|
||||
{
|
||||
Res res;
|
||||
size_t size;
|
||||
|
||||
size= size_tAlignUp(sizeof(eventClockSyncStruct), EVENT_ALIGN);
|
||||
eventClockSyncStruct.code = EventEventClockSyncCode;
|
||||
eventClockSyncStruct.size = (EventSize)size;
|
||||
EVENT_CLOCK(eventClockSyncStruct.clock);
|
||||
eventClockSyncStruct.f0 = (Word)mps_clock();
|
||||
res = (Res)mps_io_write(eventIO, (void *)&eventClockSyncStruct, size);
|
||||
if (res != ResOK)
|
||||
goto failWrite;
|
||||
|
||||
res = ResOK;
|
||||
failWrite:
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* EventFlush -- flush event buffer (perhaps to the event stream) */
|
||||
|
||||
void EventFlush(EventKind kind)
|
||||
{
|
||||
AVER(eventInited);
|
||||
AVER(NONNEGATIVE(kind));
|
||||
AVER(kind < EventKindLIMIT);
|
||||
|
||||
AVER(EventBuffer[kind] <= EventLast[kind]);
|
||||
AVER(EventLast[kind] <= EventWritten[kind]);
|
||||
AVER(EventWritten[kind] <= EventBuffer[kind] + EventBufferSIZE);
|
||||
|
||||
/* Send all pending events to the event stream. */
|
||||
EventSync();
|
||||
|
||||
/* Flush the in-memory buffer whether or not we send this buffer, so
|
||||
that we can continue to record recent events. */
|
||||
EventLast[kind] = EventWritten[kind] = EventBuffer[kind] + EventBufferSIZE;
|
||||
}
|
||||
|
||||
|
||||
/* EventSync -- synchronize the event stream with the buffers */
|
||||
|
||||
void EventSync(void)
|
||||
{
|
||||
EventKind kind;
|
||||
Bool wrote = FALSE;
|
||||
|
||||
for (kind = 0; kind < EventKindLIMIT; ++kind) {
|
||||
|
||||
/* Is event logging enabled for this kind of event, or are or are we just
|
||||
writing to the buffer for backtraces, cores, and other debugging? */
|
||||
if (BS_IS_MEMBER(EventKindControl, kind)) {
|
||||
size_t size;
|
||||
Res res;
|
||||
|
||||
AVER(EventBuffer[kind] <= EventLast[kind]);
|
||||
AVER(EventLast[kind] <= EventWritten[kind]);
|
||||
AVER(EventWritten[kind] <= EventBuffer[kind] + EventBufferSIZE);
|
||||
|
||||
size = (size_t)(EventWritten[kind] - EventLast[kind]);
|
||||
if (size > 0) {
|
||||
|
||||
/* Ensure the IO stream is open. We do this late so that no stream is
|
||||
created if no events are enabled by telemetry control. */
|
||||
if (!eventIOInited) {
|
||||
res = (Res)mps_io_create(&eventIO);
|
||||
if(res != ResOK) {
|
||||
/* TODO: Consider taking some other action if open fails. */
|
||||
return;
|
||||
}
|
||||
eventIOInited = TRUE;
|
||||
}
|
||||
|
||||
/* Writing might be faster if the size is aligned to a multiple of the
|
||||
C library or kernel's buffer size. We could pad out the buffer with
|
||||
a marker for this purpose. */
|
||||
|
||||
res = (Res)mps_io_write(eventIO, (void *)EventLast[kind], size);
|
||||
if (res == ResOK) {
|
||||
/* TODO: Consider taking some other action if a write fails. */
|
||||
EventWritten[kind] = EventLast[kind];
|
||||
wrote = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we wrote out events, send an EventClockSync event and flush
|
||||
the telemetry stream. */
|
||||
if (wrote) {
|
||||
(void)eventClockSync();
|
||||
(void)mps_io_flush(eventIO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* EventInit -- start using the event system, initialize if necessary */
|
||||
|
||||
void EventInit(void)
|
||||
{
|
||||
/* Make local enums for all event params in order to check that the indexes
|
||||
in the parameter definition macros are in order, and that parameter
|
||||
idents are unique. */
|
||||
|
||||
#define EVENT_CHECK_ENUM_PARAM(name, index, sort, ident, doc) \
|
||||
Event##name##Param##ident,
|
||||
|
||||
#define EVENT_CHECK_ENUM(X, name, code, used, kind) \
|
||||
enum Event##name##ParamEnum { \
|
||||
EVENT_##name##_PARAMS(EVENT_CHECK_ENUM_PARAM, name) \
|
||||
Event##name##ParamLIMIT \
|
||||
};
|
||||
|
||||
EVENT_LIST(EVENT_CHECK_ENUM, X)
|
||||
|
||||
/* Check consistency of the event definitions. These are all compile-time
|
||||
checks and should get optimised away. */
|
||||
|
||||
#define EVENT_PARAM_CHECK_P(name, index)
|
||||
#define EVENT_PARAM_CHECK_A(name, index)
|
||||
#define EVENT_PARAM_CHECK_W(name, index)
|
||||
#define EVENT_PARAM_CHECK_U(name, index)
|
||||
#define EVENT_PARAM_CHECK_D(name, index)
|
||||
#define EVENT_PARAM_CHECK_B(name, index)
|
||||
#define EVENT_PARAM_CHECK_S(name, index) \
|
||||
AVER(index + 1 == Event##name##ParamLIMIT); /* strings must come last */
|
||||
|
||||
#define EVENT_PARAM_CHECK(name, index, sort, ident, doc) \
|
||||
AVER(index == Event##name##Param##ident); \
|
||||
AVER(sizeof(EventF##sort) >= 0); /* check existence of type */ \
|
||||
EVENT_PARAM_CHECK_##sort(name, index)
|
||||
|
||||
#define EVENT_CHECK(X, name, code, used, kind) \
|
||||
AVER(size_tAlignUp(sizeof(Event##name##Struct), EVENT_ALIGN) \
|
||||
<= EventSizeMAX); \
|
||||
AVER(Event##name##Code == code); \
|
||||
AVER(0 <= code); \
|
||||
AVER(code <= EventCodeMAX); \
|
||||
AVER(sizeof(#name) - 1 <= EventNameMAX); \
|
||||
AVER((Bool)Event##name##Used == used); \
|
||||
AVERT(Bool, used); \
|
||||
AVER(0 <= Event##name##Kind); \
|
||||
AVER((EventKind)Event##name##Kind < EventKindLIMIT); \
|
||||
EVENT_##name##_PARAMS(EVENT_PARAM_CHECK, name)
|
||||
|
||||
EVENT_LIST(EVENT_CHECK, X);
|
||||
|
||||
/* Ensure that no event can be larger than the maximum event size. */
|
||||
AVER(EventBufferSIZE <= EventSizeMAX);
|
||||
|
||||
/* Only if this is the first call. */
|
||||
if (!eventInited) { /* See .trans.log */
|
||||
LockClaimGlobalRecursive();
|
||||
if (!eventInited) {
|
||||
EventKind kind;
|
||||
for (kind = 0; kind < EventKindLIMIT; ++kind) {
|
||||
AVER(EventLast[kind] == NULL);
|
||||
AVER(EventWritten[kind] == NULL);
|
||||
EventLast[kind] = EventWritten[kind] = EventBuffer[kind] + EventBufferSIZE;
|
||||
}
|
||||
eventInited = TRUE;
|
||||
EventKindControl = (Word)mps_lib_telemetry_control();
|
||||
EventInternSerial = (Serial)1; /* 0 is reserved */
|
||||
(void)EventInternString(MPSVersion()); /* emit version */
|
||||
EVENT7(EventInit, EVENT_VERSION_MAJOR, EVENT_VERSION_MEDIAN,
|
||||
EVENT_VERSION_MINOR, EventCodeMAX, EventNameMAX, MPS_WORD_WIDTH,
|
||||
mps_clocks_per_sec());
|
||||
/* flush these initial events to get the first ClockSync out. */
|
||||
EventSync();
|
||||
}
|
||||
LockReleaseGlobalRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* EventFinish -- stop using the event system */
|
||||
|
||||
void EventFinish(void)
|
||||
{
|
||||
AVER(eventInited);
|
||||
|
||||
EventSync();
|
||||
}
|
||||
|
||||
|
||||
/* EventControl -- Change or read control word
|
||||
*
|
||||
* Resets the bits specified in resetMask, and flips those in
|
||||
* flipMask. Returns old value.
|
||||
*
|
||||
* Operations can be implemented as follows:
|
||||
* Set(M) EventControl(M,M)
|
||||
* Reset(M) EventControl(M,0)
|
||||
* Flip(M) EventControl(0,M)
|
||||
* Read() EventControl(0,0)
|
||||
*
|
||||
* TODO: Candy-machine interface is a transgression.
|
||||
*/
|
||||
|
||||
EventControlSet EventControl(EventControlSet resetMask,
|
||||
EventControlSet flipMask)
|
||||
{
|
||||
EventControlSet oldValue = EventKindControl;
|
||||
|
||||
/* EventKindControl = (EventKindControl & ~resetMask) ^ flipMask */
|
||||
EventKindControl =
|
||||
BS_SYM_DIFF(BS_DIFF(EventKindControl, resetMask), flipMask);
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
|
||||
/* EventInternString -- emit an Intern event on the (null-term) string given */
|
||||
|
||||
EventStringId EventInternString(const char *label)
|
||||
{
|
||||
AVER(label != NULL);
|
||||
return EventInternGenString(StringLength(label), label);
|
||||
}
|
||||
|
||||
|
||||
/* EventInternGenString -- emit an Intern event on the string given */
|
||||
|
||||
EventStringId EventInternGenString(size_t len, const char *label)
|
||||
{
|
||||
EventStringId id;
|
||||
|
||||
AVER(label != NULL);
|
||||
|
||||
id = EventInternSerial;
|
||||
++EventInternSerial;
|
||||
|
||||
EVENT2S(Intern, id, len, label);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
/* EventLabelAddr -- emit event to label address with the given id */
|
||||
|
||||
void EventLabelAddr(Addr addr, EventStringId id)
|
||||
{
|
||||
AVER((Serial)id < EventInternSerial);
|
||||
|
||||
EVENT2(Label, addr, id);
|
||||
}
|
||||
|
||||
|
||||
/* EventLabelPointer -- emit event to label pointer with the given id */
|
||||
|
||||
void EventLabelPointer(Pointer pointer, EventStringId id)
|
||||
{
|
||||
AVER((Serial)id < EventInternSerial);
|
||||
|
||||
EVENT2(LabelPointer, pointer, id);
|
||||
}
|
||||
|
||||
|
||||
/* Convert event parameter sort to WriteF arguments */
|
||||
|
||||
#define EVENT_WRITE_PARAM_MOST(name, index, sort) \
|
||||
" $"#sort, (WriteF##sort)event->name.f##index,
|
||||
#define EVENT_WRITE_PARAM_A EVENT_WRITE_PARAM_MOST
|
||||
#define EVENT_WRITE_PARAM_P EVENT_WRITE_PARAM_MOST
|
||||
#define EVENT_WRITE_PARAM_U EVENT_WRITE_PARAM_MOST
|
||||
#define EVENT_WRITE_PARAM_W EVENT_WRITE_PARAM_MOST
|
||||
#define EVENT_WRITE_PARAM_D EVENT_WRITE_PARAM_MOST
|
||||
#define EVENT_WRITE_PARAM_S EVENT_WRITE_PARAM_MOST
|
||||
#define EVENT_WRITE_PARAM_B(name, index, sort) \
|
||||
" $U", (WriteFU)event->name.f##index,
|
||||
|
||||
|
||||
Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
Res res;
|
||||
|
||||
/* TODO: Some sort of EventCheck would be good */
|
||||
if (event == NULL)
|
||||
return ResFAIL;
|
||||
if (stream == NULL)
|
||||
return ResFAIL;
|
||||
|
||||
res = WriteF(stream, depth,
|
||||
"Event $P {\n", (WriteFP)event,
|
||||
" code $U\n", (WriteFU)event->any.code,
|
||||
" clock ", NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
res = EVENT_CLOCK_WRITE(stream, depth, event->any.clock);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
res = WriteF(stream, depth, "\n size $U\n", (WriteFU)event->any.size, NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
switch (event->any.code) {
|
||||
|
||||
#define EVENT_DESC_PARAM(name, index, sort, ident, doc) \
|
||||
"\n $S", (WriteFS)#ident, \
|
||||
EVENT_WRITE_PARAM_##sort(name, index, sort)
|
||||
|
||||
#define EVENT_DESC(X, name, code, used, kind) \
|
||||
case code: \
|
||||
res = WriteF(stream, depth, \
|
||||
" event \"$S\"", (WriteFS)#name, \
|
||||
EVENT_##name##_PARAMS(EVENT_DESC_PARAM, name) \
|
||||
NULL); \
|
||||
if (res != ResOK) \
|
||||
return res; \
|
||||
break;
|
||||
|
||||
EVENT_LIST(EVENT_DESC, X)
|
||||
|
||||
default:
|
||||
res = WriteF(stream, depth, " event type unknown", NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
/* TODO: Hexdump unknown event contents. */
|
||||
break;
|
||||
}
|
||||
|
||||
res = WriteF(stream, depth,
|
||||
"\n} Event $P\n", (WriteFP)event,
|
||||
NULL);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
Res EventWrite(Event event, mps_lib_FILE *stream)
|
||||
{
|
||||
Res res;
|
||||
|
||||
if (event == NULL)
|
||||
return ResFAIL;
|
||||
if (stream == NULL)
|
||||
return ResFAIL;
|
||||
|
||||
res = EVENT_CLOCK_WRITE(stream, 0, event->any.clock);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
switch (event->any.code) {
|
||||
|
||||
#define EVENT_WRITE_PARAM(name, index, sort, ident, doc) \
|
||||
EVENT_WRITE_PARAM_##sort(name, index, sort)
|
||||
|
||||
#define EVENT_WRITE(X, name, code, used, kind) \
|
||||
case code: \
|
||||
res = WriteF(stream, 0, " $S", (WriteFS)#name, \
|
||||
EVENT_##name##_PARAMS(EVENT_WRITE_PARAM, name) \
|
||||
NULL); \
|
||||
if (res != ResOK) \
|
||||
return res; \
|
||||
break;
|
||||
EVENT_LIST(EVENT_WRITE, X)
|
||||
|
||||
default:
|
||||
res = WriteF(stream, 0, " <unknown code $U>",
|
||||
(WriteFU)event->any.code, NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
/* TODO: Hexdump unknown event contents. */
|
||||
break;
|
||||
}
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
void EventDump(mps_lib_FILE *stream)
|
||||
{
|
||||
EventKind kind;
|
||||
|
||||
AVER(stream != NULL);
|
||||
|
||||
/* This can happen if there's a backtrace very early in the life of
|
||||
the MPS, and will cause an access violation if we continue. */
|
||||
if (!eventInited) {
|
||||
(void)WriteF(stream, 0, "No events\n", NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
for (kind = 0; kind < EventKindLIMIT; ++kind) {
|
||||
char *cursor = EventLast[kind];
|
||||
const char *end = EventBuffer[kind] + EventBufferSIZE;
|
||||
while (cursor < end) {
|
||||
Event event = (void *)cursor;
|
||||
/* Try to keep going even if there's an error, because this is
|
||||
used for debugging and we'll take what we can get. */
|
||||
(void)EventWrite(event, stream);
|
||||
(void)WriteF(stream, 0, "\n", NULL);
|
||||
cursor += event->any.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else /* EVENT, not */
|
||||
|
||||
|
||||
void EventSync(void)
|
||||
{
|
||||
NOOP;
|
||||
}
|
||||
|
||||
|
||||
void EventInit(void)
|
||||
{
|
||||
NOOP;
|
||||
}
|
||||
|
||||
|
||||
void EventFinish(void)
|
||||
{
|
||||
NOOP;
|
||||
}
|
||||
|
||||
|
||||
EventControlSet EventControl(EventControlSet resetMask,
|
||||
EventControlSet flipMask)
|
||||
{
|
||||
UNUSED(resetMask);
|
||||
UNUSED(flipMask);
|
||||
return BS_EMPTY(EventControlSet);
|
||||
}
|
||||
|
||||
|
||||
EventStringId EventInternString(const char *label)
|
||||
{
|
||||
UNUSED(label);
|
||||
/* EventInternString is reached in varieties without events, but the result
|
||||
is not used for anything. */
|
||||
return (EventStringId)0x4026EAC8;
|
||||
}
|
||||
|
||||
|
||||
Word EventInternGenString(size_t len, const char *label)
|
||||
{
|
||||
UNUSED(len); UNUSED(label);
|
||||
/* EventInternGenString is reached in varieties without events, but
|
||||
the result is not used for anything. */
|
||||
return (EventStringId)0x4026EAC8;
|
||||
}
|
||||
|
||||
|
||||
void EventLabelAddr(Addr addr, Word id)
|
||||
{
|
||||
UNUSED(addr);
|
||||
UNUSED(id);
|
||||
/* EventLabelAddr is reached in varieties without events, but doesn't have
|
||||
to do anything. */
|
||||
}
|
||||
|
||||
|
||||
void EventLabelPointer(Pointer pointer, Word id)
|
||||
{
|
||||
UNUSED(pointer);
|
||||
UNUSED(id);
|
||||
/* EventLabelPointer is reached in varieties without events, but
|
||||
doesn't have to do anything. */
|
||||
}
|
||||
|
||||
|
||||
Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
UNUSED(event);
|
||||
UNUSED(stream);
|
||||
UNUSED(depth);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
|
||||
Res EventWrite(Event event, mps_lib_FILE *stream)
|
||||
{
|
||||
UNUSED(event);
|
||||
UNUSED(stream);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
|
||||
void EventDump(mps_lib_FILE *stream)
|
||||
{
|
||||
UNUSED(stream);
|
||||
}
|
||||
|
||||
|
||||
#endif /* EVENT */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
234
mps/code/event.h
Normal file
234
mps/code/event.h
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
/* <code/event.h> -- Event Logging Interface
|
||||
*
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* $Id$
|
||||
*
|
||||
* READERSHIP
|
||||
*
|
||||
* .readership: MPS developers.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* .design: <design/telemetry>.
|
||||
*/
|
||||
|
||||
#ifndef event_h
|
||||
#define event_h
|
||||
|
||||
#include "eventcom.h"
|
||||
#include "mpm.h"
|
||||
#include "eventdef.h"
|
||||
#include "mpslib.h"
|
||||
|
||||
|
||||
typedef Word EventStringId;
|
||||
typedef Word EventControlSet;
|
||||
|
||||
extern void EventSync(void);
|
||||
extern void EventInit(void);
|
||||
extern void EventFinish(void);
|
||||
extern EventControlSet EventControl(EventControlSet resetMask,
|
||||
EventControlSet flipMask);
|
||||
extern EventStringId EventInternString(const char *label);
|
||||
extern EventStringId EventInternGenString(size_t, const char *label);
|
||||
extern void EventLabelAddr(Addr addr, Word id);
|
||||
extern void EventLabelPointer(Pointer pointer, Word id);
|
||||
extern void EventFlush(EventKind kind);
|
||||
extern Res EventDescribe(Event event, mps_lib_FILE *stream, Count depth);
|
||||
extern Res EventWrite(Event event, mps_lib_FILE *stream);
|
||||
extern void EventDump(mps_lib_FILE *stream);
|
||||
|
||||
|
||||
#ifdef EVENT
|
||||
|
||||
/* Event writing support */
|
||||
|
||||
extern char EventBuffer[EventKindLIMIT][EventBufferSIZE];
|
||||
extern char *EventLast[EventKindLIMIT];
|
||||
extern Word EventKindControl;
|
||||
|
||||
|
||||
/* EVENT_BEGIN -- flush buffer if necessary and write event header */
|
||||
|
||||
#define EVENT_BEGIN(name, structSize) \
|
||||
BEGIN \
|
||||
Event##name##Struct *_event; \
|
||||
EventKind _kind = Event##name##Kind; \
|
||||
size_t _size = size_tAlignUp(structSize, EVENT_ALIGN); \
|
||||
AVER(Event##name##Used); \
|
||||
if (_size > (size_t)(EventLast[Event##name##Kind] \
|
||||
- EventBuffer[Event##name##Kind])) \
|
||||
EventFlush(Event##name##Kind); \
|
||||
AVER(_size <= (size_t)(EventLast[Event##name##Kind] \
|
||||
- EventBuffer[Event##name##Kind])); \
|
||||
_event = (void *)(EventLast[Event##name##Kind] - _size); \
|
||||
_event->code = Event##name##Code; \
|
||||
_event->size = (EventSize)_size; \
|
||||
EVENT_CLOCK(_event->clock);
|
||||
|
||||
#define EVENT_END \
|
||||
EventLast[_kind] -= _size; \
|
||||
END
|
||||
|
||||
|
||||
/* EVENTn -- event emitting macros
|
||||
*
|
||||
* The macros EVENT0, EVENT1, etc. are used throughout the MPS to emit an
|
||||
* event with parameters. They work by appending the event parameters to
|
||||
* an event buffer, which is flushed to the telemetry output stream when
|
||||
* full. EVENT2S is a special case that takes a variable length string.
|
||||
*/
|
||||
|
||||
#define EVENT2S(name, p0, length, string) \
|
||||
BEGIN \
|
||||
size_t _string_len = (length); \
|
||||
size_t size; \
|
||||
AVER(_string_len <= EventStringLengthMAX); \
|
||||
size = offsetof(Event##name##Struct, f1) + _string_len + sizeof('\0'); \
|
||||
EVENT_BEGIN(name, size) \
|
||||
_event->f0 = (p0); \
|
||||
(void)mps_lib_memcpy(_event->f1, (string), _string_len); \
|
||||
_event->f1[_string_len] = '\0'; \
|
||||
EVENT_END; \
|
||||
END
|
||||
|
||||
#define EVENT0 EVENT_RECORD0
|
||||
#define EVENT1 EVENT_RECORD1
|
||||
#define EVENT2 EVENT_RECORD2
|
||||
#define EVENT3 EVENT_RECORD3
|
||||
#define EVENT4 EVENT_RECORD4
|
||||
#define EVENT5 EVENT_RECORD5
|
||||
#define EVENT6 EVENT_RECORD6
|
||||
#define EVENT7 EVENT_RECORD7
|
||||
#define EVENT8 EVENT_RECORD8
|
||||
#define EVENT9 EVENT_RECORD9
|
||||
#define EVENT10 EVENT_RECORD10
|
||||
#define EVENT11 EVENT_RECORD11
|
||||
#define EVENT12 EVENT_RECORD12
|
||||
#define EVENT13 EVENT_RECORD13
|
||||
#define EVENT14 EVENT_RECORD14
|
||||
|
||||
#else /* !EVENT */
|
||||
|
||||
#define EVENT0 EVENT_IGNORE0
|
||||
#define EVENT1 EVENT_IGNORE1
|
||||
#define EVENT2 EVENT_IGNORE2
|
||||
#define EVENT3 EVENT_IGNORE3
|
||||
#define EVENT4 EVENT_IGNORE4
|
||||
#define EVENT5 EVENT_IGNORE5
|
||||
#define EVENT6 EVENT_IGNORE6
|
||||
#define EVENT7 EVENT_IGNORE7
|
||||
#define EVENT8 EVENT_IGNORE8
|
||||
#define EVENT9 EVENT_IGNORE9
|
||||
#define EVENT10 EVENT_IGNORE10
|
||||
#define EVENT11 EVENT_IGNORE11
|
||||
#define EVENT12 EVENT_IGNORE12
|
||||
#define EVENT13 EVENT_IGNORE13
|
||||
#define EVENT14 EVENT_IGNORE14
|
||||
|
||||
#endif /* !EVENT */
|
||||
|
||||
#if EVENT_ALL
|
||||
|
||||
#define EVENT_CRITICAL0 EVENT0
|
||||
#define EVENT_CRITICAL1 EVENT1
|
||||
#define EVENT_CRITICAL2 EVENT2
|
||||
#define EVENT_CRITICAL3 EVENT3
|
||||
#define EVENT_CRITICAL4 EVENT4
|
||||
#define EVENT_CRITICAL5 EVENT5
|
||||
#define EVENT_CRITICAL6 EVENT6
|
||||
#define EVENT_CRITICAL7 EVENT7
|
||||
#define EVENT_CRITICAL8 EVENT8
|
||||
#define EVENT_CRITICAL9 EVENT9
|
||||
#define EVENT_CRITICAL10 EVENT10
|
||||
#define EVENT_CRITICAL11 EVENT11
|
||||
#define EVENT_CRITICAL12 EVENT12
|
||||
#define EVENT_CRITICAL13 EVENT13
|
||||
#define EVENT_CRITICAL14 EVENT14
|
||||
|
||||
#else /* !EVENT_ALL */
|
||||
|
||||
#define EVENT_CRITICAL0 EVENT_IGNORE0
|
||||
#define EVENT_CRITICAL1 EVENT_IGNORE1
|
||||
#define EVENT_CRITICAL2 EVENT_IGNORE2
|
||||
#define EVENT_CRITICAL3 EVENT_IGNORE3
|
||||
#define EVENT_CRITICAL4 EVENT_IGNORE4
|
||||
#define EVENT_CRITICAL5 EVENT_IGNORE5
|
||||
#define EVENT_CRITICAL6 EVENT_IGNORE6
|
||||
#define EVENT_CRITICAL7 EVENT_IGNORE7
|
||||
#define EVENT_CRITICAL8 EVENT_IGNORE8
|
||||
#define EVENT_CRITICAL9 EVENT_IGNORE9
|
||||
#define EVENT_CRITICAL10 EVENT_IGNORE10
|
||||
#define EVENT_CRITICAL11 EVENT_IGNORE11
|
||||
#define EVENT_CRITICAL12 EVENT_IGNORE12
|
||||
#define EVENT_CRITICAL13 EVENT_IGNORE13
|
||||
#define EVENT_CRITICAL14 EVENT_IGNORE14
|
||||
|
||||
#endif /* !EVENT_ALL */
|
||||
|
||||
|
||||
/* The following lines were generated with
|
||||
python -c 'for i in range(15): print("#define EVENT_RECORD{i}(name{args}) EVENT_BEGIN(name, sizeof(Event##name##Struct)) {assign} EVENT_END\n#define EVENT_IGNORE{i}(name{args}) BEGIN {unused} END".format(i=i, args="".join(map(", p{}".format, range(i))), assign=" ".join(map("_event->f{0} = (p{0});".format, range(i))), unused=" ".join(map("UNUSED(p{});".format, range(i)))))'
|
||||
*/
|
||||
#define EVENT_RECORD0(name) EVENT_BEGIN(name, sizeof(Event##name##Struct)) EVENT_END
|
||||
#define EVENT_IGNORE0(name) BEGIN END
|
||||
#define EVENT_RECORD1(name, p0) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); EVENT_END
|
||||
#define EVENT_IGNORE1(name, p0) BEGIN UNUSED(p0); END
|
||||
#define EVENT_RECORD2(name, p0, p1) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); EVENT_END
|
||||
#define EVENT_IGNORE2(name, p0, p1) BEGIN UNUSED(p0); UNUSED(p1); END
|
||||
#define EVENT_RECORD3(name, p0, p1, p2) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); EVENT_END
|
||||
#define EVENT_IGNORE3(name, p0, p1, p2) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); END
|
||||
#define EVENT_RECORD4(name, p0, p1, p2, p3) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); EVENT_END
|
||||
#define EVENT_IGNORE4(name, p0, p1, p2, p3) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); END
|
||||
#define EVENT_RECORD5(name, p0, p1, p2, p3, p4) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); EVENT_END
|
||||
#define EVENT_IGNORE5(name, p0, p1, p2, p3, p4) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); END
|
||||
#define EVENT_RECORD6(name, p0, p1, p2, p3, p4, p5) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); EVENT_END
|
||||
#define EVENT_IGNORE6(name, p0, p1, p2, p3, p4, p5) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); END
|
||||
#define EVENT_RECORD7(name, p0, p1, p2, p3, p4, p5, p6) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); EVENT_END
|
||||
#define EVENT_IGNORE7(name, p0, p1, p2, p3, p4, p5, p6) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); END
|
||||
#define EVENT_RECORD8(name, p0, p1, p2, p3, p4, p5, p6, p7) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); _event->f7 = (p7); EVENT_END
|
||||
#define EVENT_IGNORE8(name, p0, p1, p2, p3, p4, p5, p6, p7) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); UNUSED(p7); END
|
||||
#define EVENT_RECORD9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); _event->f7 = (p7); _event->f8 = (p8); EVENT_END
|
||||
#define EVENT_IGNORE9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); UNUSED(p7); UNUSED(p8); END
|
||||
#define EVENT_RECORD10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); _event->f7 = (p7); _event->f8 = (p8); _event->f9 = (p9); EVENT_END
|
||||
#define EVENT_IGNORE10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); UNUSED(p7); UNUSED(p8); UNUSED(p9); END
|
||||
#define EVENT_RECORD11(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); _event->f7 = (p7); _event->f8 = (p8); _event->f9 = (p9); _event->f10 = (p10); EVENT_END
|
||||
#define EVENT_IGNORE11(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); UNUSED(p7); UNUSED(p8); UNUSED(p9); UNUSED(p10); END
|
||||
#define EVENT_RECORD12(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); _event->f7 = (p7); _event->f8 = (p8); _event->f9 = (p9); _event->f10 = (p10); _event->f11 = (p11); EVENT_END
|
||||
#define EVENT_IGNORE12(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); UNUSED(p7); UNUSED(p8); UNUSED(p9); UNUSED(p10); UNUSED(p11); END
|
||||
#define EVENT_RECORD13(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); _event->f7 = (p7); _event->f8 = (p8); _event->f9 = (p9); _event->f10 = (p10); _event->f11 = (p11); _event->f12 = (p12); EVENT_END
|
||||
#define EVENT_IGNORE13(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); UNUSED(p7); UNUSED(p8); UNUSED(p9); UNUSED(p10); UNUSED(p11); UNUSED(p12); END
|
||||
#define EVENT_RECORD14(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13) EVENT_BEGIN(name, sizeof(Event##name##Struct)) _event->f0 = (p0); _event->f1 = (p1); _event->f2 = (p2); _event->f3 = (p3); _event->f4 = (p4); _event->f5 = (p5); _event->f6 = (p6); _event->f7 = (p7); _event->f8 = (p8); _event->f9 = (p9); _event->f10 = (p10); _event->f11 = (p11); _event->f12 = (p12); _event->f13 = (p13); EVENT_END
|
||||
#define EVENT_IGNORE14(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13) BEGIN UNUSED(p0); UNUSED(p1); UNUSED(p2); UNUSED(p3); UNUSED(p4); UNUSED(p5); UNUSED(p6); UNUSED(p7); UNUSED(p8); UNUSED(p9); UNUSED(p10); UNUSED(p11); UNUSED(p12); UNUSED(p13); END
|
||||
|
||||
#endif /* event_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
366
mps/code/eventcnv.c
Normal file
366
mps/code/eventcnv.c
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/* eventcnv.c: Simple event log converter
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* This is a command-line tool that converts a binary format telemetry output
|
||||
* stream from the MPS into a more-portable textual format.
|
||||
*
|
||||
* eventcnv can only read binary-format files that come from an MPS
|
||||
* compiled on the same platform, whereas the text-format files it
|
||||
* produces can be processed on any platform.
|
||||
*
|
||||
* The default MPS library will write a telemetry stream to a file
|
||||
* when the environment variable MPS_TELEMETRY_CONTROL is set to an
|
||||
* integer whose bits select event kinds. For example:
|
||||
*
|
||||
* MPS_TELEMETRY_CONTROL=7 amcss
|
||||
*
|
||||
* will run the amcss test program and emit a telemetry file with
|
||||
* event kinds 0, 1, 2. The file can then be converted into a sorted
|
||||
* text format log with a command like:
|
||||
*
|
||||
* eventcnv | sort > mps-events.txt
|
||||
*
|
||||
* These text-format files have one line per event, and can be
|
||||
* manipulated by various programs systems in the usual Unix way.
|
||||
*
|
||||
* The binary telemetry filename can be specified with a -f
|
||||
* command-line argument (use -f - to specify standard input). If no
|
||||
* filename is specified on the command line, the environment variable
|
||||
* MPS_TELEMETRY_FILENAME is consulted (this is the same environment
|
||||
* variable used to specify the telemetry file to the MPS library).
|
||||
* If the environment variable does not exist, the default filename of
|
||||
* "mpsio.log" is used.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "eventdef.h"
|
||||
#include "eventcom.h"
|
||||
#include "testlib.h" /* for ulongest_t and associated print formats */
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
#include <stdio.h> /* for printf */
|
||||
#include <stdlib.h> /* for EXIT_FAILURE */
|
||||
#include <assert.h> /* for assert */
|
||||
#include <string.h> /* for strcmp */
|
||||
#include "mpstd.h"
|
||||
|
||||
#define DEFAULT_TELEMETRY_FILENAME "mpsio.log"
|
||||
#define TELEMETRY_FILENAME_ENVAR "MPS_TELEMETRY_FILENAME"
|
||||
|
||||
static EventClock eventTime; /* current event time */
|
||||
static const char *prog; /* program name */
|
||||
|
||||
/* Errors and Warnings */
|
||||
|
||||
/* fevwarn -- flush stdout, write message to stderr */
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 2, 0))
|
||||
static void fevwarn(const char *prefix, const char *format, va_list args)
|
||||
{
|
||||
(void)fflush(stdout); /* sync */
|
||||
(void)fprintf(stderr, "%s: %s @", prog, prefix);
|
||||
(void)EVENT_CLOCK_PRINT(stderr, eventTime);
|
||||
(void)fprintf(stderr, " ");
|
||||
(void)vfprintf(stderr, format, args);
|
||||
(void)fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
/* evwarn -- flush stdout, warn to stderr */
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 1, 2))
|
||||
static void evwarn(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
fevwarn("Warning", format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/* everror -- flush stdout, message to stderr, exit */
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 1, 2))
|
||||
static void everror(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
fevwarn("Error", format, args);
|
||||
va_end(args);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
/* usage -- usage message */
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
(void)fprintf(stderr, "Usage: %s [-f logfile] [-h]\n"
|
||||
"See \"Telemetry\" in the reference manual for instructions.\n",
|
||||
prog);
|
||||
}
|
||||
|
||||
|
||||
/* usageError -- explain usage and error */
|
||||
|
||||
static void usageError(void)
|
||||
{
|
||||
usage();
|
||||
everror("Bad usage");
|
||||
}
|
||||
|
||||
|
||||
/* parseArgs -- parse command line arguments, return log file name */
|
||||
|
||||
static char *parseArgs(int argc, char *argv[])
|
||||
{
|
||||
char *name = NULL;
|
||||
int i = 1;
|
||||
|
||||
if (argc >= 1)
|
||||
prog = argv[0];
|
||||
else
|
||||
prog = "unknown";
|
||||
|
||||
while (i < argc) { /* consider argument i */
|
||||
if (argv[i][0] == '-') { /* it's an option argument */
|
||||
switch (argv[i][1]) {
|
||||
case 'f': /* file name */
|
||||
++ i;
|
||||
if (i == argc)
|
||||
usageError();
|
||||
else
|
||||
name = argv[i];
|
||||
break;
|
||||
case '?': case 'h': /* help */
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
usageError();
|
||||
}
|
||||
} /* if option */
|
||||
++ i;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/* Printing routines */
|
||||
|
||||
static void printHex(ulongest_t val)
|
||||
{
|
||||
printf(" %"PRIXLONGEST, (ulongest_t)val);
|
||||
}
|
||||
|
||||
#define printParamP(p) printHex((ulongest_t)p)
|
||||
#define printParamA(a) printHex((ulongest_t)a)
|
||||
#define printParamU(u) printHex((ulongest_t)u)
|
||||
#define printParamW(w) printHex((ulongest_t)w)
|
||||
#define printParamB(b) printHex((ulongest_t)b)
|
||||
|
||||
static void printParamD(double d)
|
||||
{
|
||||
printf(" %.10G", d);
|
||||
}
|
||||
|
||||
static void printParamS(const char *str)
|
||||
{
|
||||
size_t i;
|
||||
putchar(' ');
|
||||
putchar('"');
|
||||
for (i = 0; str[i] != '\0'; ++i) {
|
||||
char c = str[i];
|
||||
if (c == '"' || c == '\\')
|
||||
putchar('\\');
|
||||
putchar(c);
|
||||
}
|
||||
putchar('"');
|
||||
}
|
||||
|
||||
|
||||
/* EventRead -- read one event from the file */
|
||||
|
||||
static Res eventRead(Bool *eofOut, EventUnion *event, FILE *stream)
|
||||
{
|
||||
size_t n;
|
||||
size_t rest;
|
||||
|
||||
/* Read the prefix common to all event structures, in order to decode the
|
||||
event size. */
|
||||
n = fread(&event->any, sizeof(event->any), 1, stream);
|
||||
if (n < 1) {
|
||||
if (feof(stream)) {
|
||||
*eofOut = TRUE;
|
||||
return ResOK;
|
||||
}
|
||||
return ResIO;
|
||||
}
|
||||
|
||||
if (event->any.size < sizeof(event->any))
|
||||
return ResFAIL; /* invalid size: too small */
|
||||
|
||||
if (event->any.size > sizeof(*event))
|
||||
return ResFAIL; /* invalid size: too large */
|
||||
|
||||
/* Read the rest of the event. */
|
||||
rest = event->any.size - sizeof(event->any);
|
||||
if (rest > 0) {
|
||||
n = fread((char *)event + sizeof(event->any), rest, 1, stream);
|
||||
if (n < 1) {
|
||||
if (feof(stream))
|
||||
return ResFAIL; /* truncated event */
|
||||
else
|
||||
return ResIO;
|
||||
}
|
||||
}
|
||||
|
||||
*eofOut = FALSE;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
/* readLog -- read and parse log */
|
||||
|
||||
static void readLog(FILE *stream)
|
||||
{
|
||||
for(;;) { /* loop for each event */
|
||||
EventUnion eventUnion;
|
||||
Event event = &eventUnion;
|
||||
EventCode code;
|
||||
Res res;
|
||||
Bool eof = FALSE; /* suppress warnings about uninitialized use */
|
||||
|
||||
/* Read and parse event. */
|
||||
res = eventRead(&eof, event, stream);
|
||||
if (res == ResFAIL)
|
||||
everror("Truncated log");
|
||||
else if (res == ResIO)
|
||||
everror("I/O error reading log");
|
||||
else if (res != ResOK)
|
||||
everror("Unknown error reading log");
|
||||
if (eof)
|
||||
break;
|
||||
|
||||
eventTime = event->any.clock;
|
||||
code = event->any.code;
|
||||
|
||||
/* Special handling for some events, prior to text output */
|
||||
|
||||
switch(code) {
|
||||
case EventEventInitCode:
|
||||
if ((event->EventInit.f0 != EVENT_VERSION_MAJOR) ||
|
||||
(event->EventInit.f1 != EVENT_VERSION_MEDIAN) ||
|
||||
(event->EventInit.f2 != EVENT_VERSION_MINOR))
|
||||
evwarn("Event log version does not match: %d.%d.%d vs %d.%d.%d",
|
||||
event->EventInit.f0,
|
||||
event->EventInit.f1,
|
||||
event->EventInit.f2,
|
||||
EVENT_VERSION_MAJOR,
|
||||
EVENT_VERSION_MEDIAN,
|
||||
EVENT_VERSION_MINOR);
|
||||
|
||||
if (event->EventInit.f3 > EventCodeMAX)
|
||||
evwarn("Event log may contain unknown events with codes from %d to %d",
|
||||
EventCodeMAX+1, event->EventInit.f3);
|
||||
|
||||
if (event->EventInit.f5 != MPS_WORD_WIDTH)
|
||||
/* This probably can't happen; other things will break
|
||||
* before we get here */
|
||||
evwarn("Event log has incompatible word width: %d instead of %d",
|
||||
event->EventInit.f5,
|
||||
MPS_WORD_WIDTH);
|
||||
break;
|
||||
default:
|
||||
/* No special treatment needed. */
|
||||
break;
|
||||
}
|
||||
|
||||
(void)EVENT_CLOCK_PRINT(stdout, eventTime);
|
||||
printf(" %4X", (unsigned)code);
|
||||
|
||||
switch (code) {
|
||||
#define EVENT_PARAM_PRINT(name, index, sort, ident, doc) \
|
||||
printParam##sort(event->name.f##index);
|
||||
#define EVENT_PRINT(X, name, code, used, kind) \
|
||||
case code: \
|
||||
EVENT_##name##_PARAMS(EVENT_PARAM_PRINT, name) \
|
||||
break;
|
||||
EVENT_LIST(EVENT_PRINT, X)
|
||||
default:
|
||||
evwarn("Unknown event code %d", code);
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
(void)fflush(stdout);
|
||||
} /* while(!feof(input)) */
|
||||
}
|
||||
|
||||
|
||||
/* CHECKCONV -- check t2 can be cast to t1 without loss */
|
||||
|
||||
#define CHECKCONV(t1, t2) \
|
||||
(sizeof(t1) >= sizeof(t2))
|
||||
|
||||
|
||||
/* main */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *filename;
|
||||
FILE *input;
|
||||
|
||||
assert(CHECKCONV(ulongest_t, Word));
|
||||
assert(CHECKCONV(ulongest_t, Addr));
|
||||
assert(CHECKCONV(ulongest_t, void *));
|
||||
assert(CHECKCONV(ulongest_t, EventCode));
|
||||
|
||||
filename = parseArgs(argc, argv);
|
||||
if (!filename) {
|
||||
filename = getenv(TELEMETRY_FILENAME_ENVAR);
|
||||
if(!filename)
|
||||
filename = DEFAULT_TELEMETRY_FILENAME;
|
||||
}
|
||||
|
||||
if (strcmp(filename, "-") == 0)
|
||||
input = stdin;
|
||||
else {
|
||||
input = fopen(filename, "rb");
|
||||
if (input == NULL)
|
||||
everror("unable to open \"%s\"\n", filename);
|
||||
}
|
||||
|
||||
readLog(input);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
168
mps/code/eventcom.h
Normal file
168
mps/code/eventcom.h
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/* <code/eventcom.h> -- Event Logging Common Definitions
|
||||
*
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* $Id$
|
||||
*
|
||||
* .sources: mps.design.telemetry
|
||||
*/
|
||||
|
||||
#ifndef eventcom_h
|
||||
#define eventcom_h
|
||||
|
||||
#include <limits.h>
|
||||
#include "mpmtypes.h" /* for Word */
|
||||
#include "eventdef.h"
|
||||
#include "clock.h"
|
||||
|
||||
|
||||
/* Event Kinds --- see <design/telemetry>
|
||||
*
|
||||
* All events are classified as being of one event type.
|
||||
* They are small enough to be able to be used as members of a bit set.
|
||||
*/
|
||||
|
||||
#define EventKindENUM(ENUM, X) \
|
||||
ENUM(X, Arena, "Per space or arena") \
|
||||
ENUM(X, Pool, "Per pool") \
|
||||
ENUM(X, Trace, "Per trace or scan") \
|
||||
ENUM(X, Seg, "Per seg") \
|
||||
ENUM(X, Ref, "Per ref or fix") \
|
||||
ENUM(X, Object, "Per alloc or object") \
|
||||
ENUM(X, User, "User-invoked")
|
||||
|
||||
#define ENUM_DECLARE(name) \
|
||||
enum name##Enum { \
|
||||
name##ENUM(ENUM_DECLARE_ROW, name) \
|
||||
name##LIMIT \
|
||||
};
|
||||
|
||||
#define ENUM_DECLARE_ROW(enumName, rowName, rowDoc) \
|
||||
enumName##rowName,
|
||||
|
||||
ENUM_DECLARE(EventKind)
|
||||
|
||||
|
||||
/* Event type definitions
|
||||
*
|
||||
* Various constants for each event type to describe them, so that they
|
||||
* can easily be looked up from macros by name.
|
||||
*/
|
||||
|
||||
/* Note that enum values can be up to fifteen bits long portably. */
|
||||
#define EVENT_ENUM(X, name, code, used, kind) \
|
||||
Event##name##Code = code, \
|
||||
Event##name##Used = used, \
|
||||
Event##name##Kind = EventKind##kind,
|
||||
|
||||
enum EventDefinitionsEnum {
|
||||
EVENT_LIST(EVENT_ENUM, X)
|
||||
/* suppress comma-at-end-of-enum warning */
|
||||
EventEnumWarningSuppressor = USHRT_MAX
|
||||
};
|
||||
|
||||
|
||||
/* Event*Struct -- Event Structures
|
||||
*
|
||||
* Declare the structures that are used to encode events in the internal event
|
||||
* buffers and on the binary telemetry output stream.
|
||||
*/
|
||||
|
||||
/* Types for common event fields */
|
||||
typedef unsigned short EventCode;
|
||||
typedef unsigned EventKind;
|
||||
typedef unsigned short EventSize;
|
||||
#define EventSizeMAX USHRT_MAX
|
||||
|
||||
/* Common prefix for all event structures. The size field allows an event
|
||||
reader to skip over events whose codes it does not recognise. */
|
||||
#define EVENT_ANY_FIELDS(X) \
|
||||
X(EventCode, code, "encoding of the event type") \
|
||||
X(EventSize, size, "allows reader to skip events of unknown code") \
|
||||
X(EventClock, clock, "when the event occurred")
|
||||
|
||||
#define EVENT_ANY_STRUCT_FIELD(TYPE, NAME, DOC) TYPE NAME;
|
||||
|
||||
typedef struct EventAnyStruct {
|
||||
EVENT_ANY_FIELDS(EVENT_ANY_STRUCT_FIELD)
|
||||
} EventAnyStruct;
|
||||
|
||||
/* Event field types, for indexing by macro on the event parameter sort */
|
||||
typedef void *EventFP; /* pointer to C object */
|
||||
typedef Addr EventFA; /* address on the heap */
|
||||
typedef Word EventFW; /* word */
|
||||
typedef unsigned EventFU; /* unsigned integer */
|
||||
typedef char EventFS[EventStringLengthMAX + sizeof('\0')]; /* string */
|
||||
typedef double EventFD; /* double */
|
||||
typedef unsigned char EventFB; /* Boolean */
|
||||
|
||||
/* Event packing bitfield specifiers */
|
||||
#define EventFP_BITFIELD
|
||||
#define EventFA_BITFIELD
|
||||
#define EventFW_BITFIELD
|
||||
#define EventFU_BITFIELD
|
||||
#define EventFS_BITFIELD
|
||||
#define EventFD_BITFIELD
|
||||
#define EventFB_BITFIELD
|
||||
|
||||
#define EVENT_STRUCT_FIELD(X, index, sort, ident, doc) \
|
||||
EventF##sort f##index EventF##sort##_BITFIELD;
|
||||
|
||||
#define EVENT_STRUCT(X, name, _code, used, kind) \
|
||||
typedef struct Event##name##Struct { \
|
||||
EVENT_ANY_FIELDS(EVENT_ANY_STRUCT_FIELD) \
|
||||
EVENT_##name##_PARAMS(EVENT_STRUCT_FIELD, X) \
|
||||
} Event##name##Struct;
|
||||
|
||||
EVENT_LIST(EVENT_STRUCT, X)
|
||||
|
||||
/* Maximum alignment requirement of any event type. */
|
||||
#define EVENT_ALIGN (sizeof(EventFP))
|
||||
|
||||
|
||||
/* Event -- event union type
|
||||
*
|
||||
* Event is the type of a pointer to EventUnion, which is a union of all
|
||||
* event structures. This can be used as the type of any event, decoded
|
||||
* by examining event->any.code.
|
||||
*/
|
||||
|
||||
#define EVENT_UNION_MEMBER(X, name, code, used, kind) \
|
||||
Event##name##Struct name;
|
||||
|
||||
typedef union EventUnion {
|
||||
EventAnyStruct any;
|
||||
EVENT_LIST(EVENT_UNION_MEMBER, X)
|
||||
} EventUnion, *Event;
|
||||
|
||||
|
||||
#endif /* eventcom_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
708
mps/code/eventdef.h
Normal file
708
mps/code/eventdef.h
Normal file
|
|
@ -0,0 +1,708 @@
|
|||
/* <code/eventdef.h> -- Event Logging Definitions
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .source: <design/telemetry>
|
||||
*
|
||||
* .desc: This file declares macros that define the types of events and their
|
||||
* properties.
|
||||
*
|
||||
* TRANSGRESSIONS
|
||||
*
|
||||
* .kind.abuse: A few events have a kind which is not obvious from the
|
||||
* type of the objects that the event relates to. They are given the
|
||||
* kind that that have on the grounds of expected use. The kinds are
|
||||
* used in controlling the overall volume of telemetry and these events are
|
||||
* given kinds so that they are grouped under the same control as events
|
||||
* you are likely to want to see them with. (So for example, lots of
|
||||
* scanner events have the same kind, Seg, because if you are interested
|
||||
* in one then you're probably interested in them all and it's a similar
|
||||
* amount of data).
|
||||
*/
|
||||
|
||||
#ifndef eventdef_h
|
||||
#define eventdef_h
|
||||
|
||||
|
||||
/* EVENT_VERSION_* -- three part version number
|
||||
*
|
||||
* Increment the minor version when adding new events,
|
||||
* the median version when changing an existing event,
|
||||
* and the major version when changing the format of the event file.
|
||||
*
|
||||
* These are passed as parameters to the EventInit event at the start
|
||||
* of a telemetry stream, allowing that stream to be identified.
|
||||
*/
|
||||
|
||||
#define EVENT_VERSION_MAJOR ((unsigned)2)
|
||||
#define EVENT_VERSION_MEDIAN ((unsigned)0)
|
||||
#define EVENT_VERSION_MINOR ((unsigned)0)
|
||||
|
||||
|
||||
/* EVENT_LIST -- list of event types and general properties
|
||||
*
|
||||
* The columns are:
|
||||
*
|
||||
* 1. Type: The name of the event type, without the leading "Event";
|
||||
* 2. Code: The unique 16-bit code associated with this event type;
|
||||
* 3. Used: Whether this event type is used at all;
|
||||
* 4. Kind: Category into which this event falls, without the "EventKind";
|
||||
*
|
||||
* When you retire an event type, don't delete it from the list, but
|
||||
* set the "Used" column to FALSE. This serves as documentation for
|
||||
* what the event code means in older logs, and prevents the codes
|
||||
* being re-used. <design/telemetry/#.reg.code>.
|
||||
*
|
||||
* When you add an event type, you must also add an EVENT_*_PARAMS
|
||||
* macro specifying its parameters.
|
||||
*
|
||||
* TODO: Add a doc string to each event type.
|
||||
*/
|
||||
|
||||
#define EventNameMAX ((size_t)19)
|
||||
#define EventCodeMAX ((EventCode)0x005c)
|
||||
|
||||
#define EVENT_LIST(EVENT, X) \
|
||||
/* 0123456789012345678 <- don't exceed without changing EventNameMAX */ \
|
||||
EVENT(X, AMCScanNailed , 0x0001, TRUE, Seg) \
|
||||
EVENT(X, AWLDeclineSeg , 0x0002, TRUE, Seg) \
|
||||
EVENT(X, AWLDeclineTotal , 0x0003, TRUE, Seg) \
|
||||
EVENT(X, ArenaAccessBegin , 0x0004, TRUE, Arena) \
|
||||
EVENT(X, ArenaAccessEnd , 0x0005, TRUE, Arena) \
|
||||
EVENT(X, ArenaAlloc , 0x0006, TRUE, Arena) \
|
||||
EVENT(X, ArenaAllocFail , 0x0007, TRUE, Arena) \
|
||||
EVENT(X, ArenaCreateCL , 0x0008, TRUE, Arena) \
|
||||
EVENT(X, ArenaCreateVM , 0x0009, TRUE, Arena) \
|
||||
EVENT(X, ArenaDestroy , 0x000a, TRUE, Arena) \
|
||||
EVENT(X, ArenaExtend , 0x000b, TRUE, Arena) \
|
||||
EVENT(X, ArenaFree , 0x000c, TRUE, Arena) \
|
||||
EVENT(X, ArenaPollBegin , 0x000d, TRUE, Arena) \
|
||||
EVENT(X, ArenaPollEnd , 0x000e, TRUE, Arena) \
|
||||
EVENT(X, ArenaSetEmergency , 0x000f, TRUE, Arena) \
|
||||
EVENT(X, ArenaSetSpare , 0x0010, TRUE, Arena) \
|
||||
EVENT(X, ArenaUseFreeZone , 0x0011, TRUE, Arena) \
|
||||
EVENT(X, BufferCommit , 0x0012, TRUE, Object) \
|
||||
EVENT(X, BufferEmpty , 0x0013, TRUE, Seg) \
|
||||
EVENT(X, BufferFill , 0x0014, TRUE, Seg) \
|
||||
EVENT(X, BufferFinish , 0x0015, TRUE, Pool) /* see .kind.abuse */ \
|
||||
EVENT(X, BufferInit , 0x0016, TRUE, Pool) /* see .kind.abuse */ \
|
||||
EVENT(X, BufferInitRank , 0x0017, TRUE, Pool) \
|
||||
EVENT(X, BufferInitSeg , 0x0018, TRUE, Pool) \
|
||||
EVENT(X, BufferReserve , 0x0019, TRUE, Object) \
|
||||
EVENT(X, ChainCondemnAuto , 0x001a, TRUE, Trace) \
|
||||
EVENT(X, CommitLimitSet , 0x001b, TRUE, Arena) \
|
||||
EVENT(X, EventClockSync , 0x001c, TRUE, Arena) \
|
||||
EVENT(X, EventInit , 0x001d, TRUE, Arena) \
|
||||
EVENT(X, GenFinish , 0x001e, TRUE, Arena) \
|
||||
EVENT(X, GenInit , 0x001f, TRUE, Arena) \
|
||||
EVENT(X, GenZoneSet , 0x0020, TRUE, Arena) \
|
||||
EVENT(X, Intern , 0x0021, TRUE, User) \
|
||||
EVENT(X, Label , 0x0022, TRUE, User) \
|
||||
EVENT(X, LabelPointer , 0x0023, TRUE, User) \
|
||||
EVENT(X, LandInit , 0x0024, TRUE, Pool) \
|
||||
EVENT(X, MessagesDropped , 0x0025, TRUE, Arena) \
|
||||
EVENT(X, MessagesExist , 0x0026, TRUE, Arena) \
|
||||
EVENT(X, MeterInit , 0x0027, TRUE, Pool) \
|
||||
EVENT(X, MeterValues , 0x0028, TRUE, Pool) \
|
||||
EVENT(X, PauseTimeSet , 0x0029, TRUE, Arena) \
|
||||
EVENT(X, PoolAlloc , 0x002a, TRUE, Object) \
|
||||
EVENT(X, PoolFinish , 0x002b, TRUE, Pool) \
|
||||
EVENT(X, PoolFree , 0x002c, TRUE, Object) \
|
||||
EVENT(X, PoolInit , 0x002d, TRUE, Pool) \
|
||||
EVENT(X, PoolInitAMC , 0x002e, TRUE, Pool) \
|
||||
EVENT(X, PoolInitAMCZ , 0x002f, TRUE, Pool) \
|
||||
EVENT(X, PoolInitAMS , 0x0030, TRUE, Pool) \
|
||||
EVENT(X, PoolInitAWL , 0x0031, TRUE, Pool) \
|
||||
EVENT(X, PoolInitLO , 0x0032, TRUE, Pool) \
|
||||
EVENT(X, PoolInitMFS , 0x0033, TRUE, Pool) \
|
||||
EVENT(X, PoolInitMVFF , 0x0034, TRUE, Pool) \
|
||||
EVENT(X, PoolInitMVT , 0x0035, TRUE, Pool) \
|
||||
EVENT(X, PoolInitSNC , 0x0036, TRUE, Pool) \
|
||||
EVENT(X, RootScan , 0x0037, TRUE, Seg) /* see .kind.abuse */ \
|
||||
EVENT(X, SegAlloc , 0x0038, TRUE, Seg) \
|
||||
EVENT(X, SegAllocFail , 0x0039, TRUE, Seg) \
|
||||
EVENT(X, SegFree , 0x003a, TRUE, Seg) \
|
||||
EVENT(X, SegMerge , 0x003b, TRUE, Seg) \
|
||||
EVENT(X, SegReclaim , 0x003c, TRUE, Seg) \
|
||||
EVENT(X, SegScan , 0x003d, TRUE, Seg) \
|
||||
EVENT(X, SegSetGrey , 0x003e, TRUE, Seg) \
|
||||
EVENT(X, SegSetSummary , 0x003f, TRUE, Seg) \
|
||||
EVENT(X, SegSplit , 0x0040, TRUE, Seg) \
|
||||
EVENT(X, TraceAccess , 0x0041, TRUE, Seg) \
|
||||
EVENT(X, TraceBandAdvance , 0x0042, TRUE, Trace) \
|
||||
EVENT(X, TraceCondemnAll , 0x0043, TRUE, Trace) \
|
||||
EVENT(X, TraceCreate , 0x0044, TRUE, Trace) \
|
||||
EVENT(X, TraceCreatePoolGen , 0x0045, TRUE, Trace) \
|
||||
EVENT(X, TraceDestroy , 0x0046, TRUE, Trace) \
|
||||
EVENT(X, TraceEndGen , 0x0047, TRUE, Trace) \
|
||||
EVENT(X, TraceFindGrey , 0x0048, TRUE, Seg) \
|
||||
EVENT(X, TraceFix , 0x0049, TRUE, Ref) \
|
||||
EVENT(X, TraceFixSeg , 0x004a, TRUE, Ref) \
|
||||
EVENT(X, TraceFlipBegin , 0x004b, TRUE, Trace) \
|
||||
EVENT(X, TraceFlipEnd , 0x004c, TRUE, Trace) \
|
||||
EVENT(X, TraceReclaim , 0x004d, TRUE, Trace) \
|
||||
EVENT(X, TraceScanArea , 0x004e, TRUE, Seg) /* see .kind.abuse */ \
|
||||
EVENT(X, TraceScanAreaTagged, 0x004f, TRUE, Seg) /* see .kind.abuse */ \
|
||||
EVENT(X, TraceScanSingleRef , 0x0050, TRUE, Seg) /* see .kind.abuse */ \
|
||||
EVENT(X, TraceStart , 0x0051, TRUE, Trace) \
|
||||
EVENT(X, TraceStatFix , 0x0052, TRUE, Trace) \
|
||||
EVENT(X, TraceStatReclaim , 0x0053, TRUE, Trace) \
|
||||
EVENT(X, TraceStatScan , 0x0054, TRUE, Trace) \
|
||||
EVENT(X, VMArenaExtendDone , 0x0055, TRUE, Arena) \
|
||||
EVENT(X, VMArenaExtendFail , 0x0056, TRUE, Arena) \
|
||||
EVENT(X, VMArenaExtendStart , 0x0057, TRUE, Arena) \
|
||||
EVENT(X, VMCompact , 0x0058, TRUE, Arena) \
|
||||
EVENT(X, VMFinish , 0x0059, TRUE, Arena) \
|
||||
EVENT(X, VMInit , 0x005a, TRUE, Arena) \
|
||||
EVENT(X, VMMap , 0x005b, TRUE, Seg) \
|
||||
EVENT(X, VMUnmap , 0x005c, TRUE, Seg)
|
||||
|
||||
|
||||
/* Remember to update EventNameMAX and EventCodeMAX above!
|
||||
(These are checked in EventInit.) */
|
||||
|
||||
|
||||
/* EVENT_*_PARAMS -- definition of event parameters
|
||||
*
|
||||
* For each event type in EVENT_LIST, these macros list the parameters of
|
||||
* the event. The columns are:
|
||||
*
|
||||
* 1. index (used to define numeric field names);
|
||||
* 2. sort (Pointer, Addr, Word, Unsigned, String, Double, Bool);
|
||||
* 3. identifier (for display or use in code).
|
||||
* 4. documentation.
|
||||
*/
|
||||
|
||||
#define EVENT_AMCScanNailed_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, loops, "number of times around the loop") \
|
||||
PARAM(X, 1, W, summary, "summary of segment being scanned") \
|
||||
PARAM(X, 2, W, white, "scan state white set") \
|
||||
PARAM(X, 3, W, unfixed, "scan state unfixed summary") \
|
||||
PARAM(X, 4, W, fixed, "scan state fixed summary") \
|
||||
PARAM(X, 5, W, refset, "scan state refset")
|
||||
|
||||
#define EVENT_AWLDeclineSeg_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, seg, "segment declined single access") \
|
||||
PARAM(X, 1, W, singleAccesses, "single accesses this cycle")
|
||||
|
||||
#define EVENT_AWLDeclineTotal_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, seg, "segment declined single access") \
|
||||
PARAM(X, 1, W, succAccesses, "total successive accesses")
|
||||
|
||||
#define EVENT_ArenaAccessBegin_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, P, addr, "address that was accessed") \
|
||||
PARAM(X, 2, U, mode, "set of access modes")
|
||||
|
||||
#define EVENT_ArenaAccessEnd_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena")
|
||||
|
||||
#define EVENT_ArenaAlloc_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, P, baseTract, "first allocated tract") \
|
||||
PARAM(X, 2, A, base, "base of the allocated block") \
|
||||
PARAM(X, 3, W, size, "size of the allocated block in bytes") \
|
||||
PARAM(X, 4, P, pool, "pool that requested the allocation")
|
||||
|
||||
#define EVENT_ArenaAllocFail_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, W, size, "requested allocation size") \
|
||||
PARAM(X, 2, P, pool, "pool that requested allocation") \
|
||||
PARAM(X, 3, U, res, "result code")
|
||||
|
||||
#define EVENT_ArenaCreateCL_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, W, size, "size of memory given to arena in bytes") \
|
||||
PARAM(X, 2, A, base, "base address of memory given to arena") \
|
||||
PARAM(X, 3, W, grainSize, "arena's grain size in bytes") \
|
||||
PARAM(X, 4, P, arenaClass, "arena's class") \
|
||||
PARAM(X, 5, W, systemPools, "number of system pools") \
|
||||
PARAM(X, 6, U, serial, "arena's serial number")
|
||||
|
||||
#define EVENT_ArenaCreateVM_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, W, userSize, "requested address space in bytes") \
|
||||
PARAM(X, 2, W, chunkSize, "arena's chunk size in bytes") \
|
||||
PARAM(X, 3, W, grainSize, "arena's grain size in bytes") \
|
||||
PARAM(X, 4, P, arenaClass, "arena's class") \
|
||||
PARAM(X, 5, W, systemPools, "number of system pools") \
|
||||
PARAM(X, 6, U, serial, "arena's serial number")
|
||||
|
||||
#define EVENT_ArenaDestroy_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena")
|
||||
|
||||
#define EVENT_ArenaExtend_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, A, base, "base of new chunk") \
|
||||
PARAM(X, 2, W, size, "size of new chunk")
|
||||
|
||||
#define EVENT_ArenaFree_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, A, base, "base of the freed block") \
|
||||
PARAM(X, 2, W, size, "size of the freed block in bytes") \
|
||||
PARAM(X, 3, P, pool, "pool that freed the block")
|
||||
|
||||
#define EVENT_ArenaPollBegin_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "arena about to be polled")
|
||||
|
||||
#define EVENT_ArenaPollEnd_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "arena that was polled") \
|
||||
PARAM(X, 1, B, workWasDone, "any collection work done in poll?")
|
||||
|
||||
#define EVENT_ArenaSetEmergency_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, B, emergency, "emergency mode?")
|
||||
|
||||
#define EVENT_ArenaSetSpare_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, D, spare, "spare committed fraction")
|
||||
|
||||
#define EVENT_ArenaUseFreeZone_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, W, zoneSet, "zones that aren't free any longer")
|
||||
|
||||
#define EVENT_BufferCommit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer") \
|
||||
PARAM(X, 1, A, p, "committed object") \
|
||||
PARAM(X, 2, W, size, "size of committed object") \
|
||||
PARAM(X, 3, A, clientClass, "format's class of 0 if no format")
|
||||
|
||||
#define EVENT_BufferEmpty_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer") \
|
||||
PARAM(X, 1, W, spare, "remaining free memory")
|
||||
|
||||
#define EVENT_BufferFill_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer") \
|
||||
PARAM(X, 1, W, size, "size of client request") \
|
||||
PARAM(X, 2, A, base, "base of pool allocation") \
|
||||
PARAM(X, 3, W, filled, "size of pool allocation")
|
||||
|
||||
#define EVENT_BufferFinish_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer")
|
||||
|
||||
#define EVENT_BufferInit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer") \
|
||||
PARAM(X, 1, P, pool, "buffer's pool") \
|
||||
PARAM(X, 2, B, isMutator, "belongs to client program?")
|
||||
|
||||
#define EVENT_BufferInitRank_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer") \
|
||||
PARAM(X, 1, P, pool, "buffer's pool") \
|
||||
PARAM(X, 2, B, isMutator, "belongs to client program?") \
|
||||
PARAM(X, 3, U, rank, "rank of references in buffer")
|
||||
|
||||
#define EVENT_BufferInitSeg_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer") \
|
||||
PARAM(X, 1, P, pool, "buffer's pool") \
|
||||
PARAM(X, 2, B, isMutator, "belongs to client program?")
|
||||
|
||||
#define EVENT_BufferReserve_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, buffer, "the buffer") \
|
||||
PARAM(X, 1, A, init, "buffer's init pointer") \
|
||||
PARAM(X, 2, W, size, "size of client request")
|
||||
|
||||
#define EVENT_ChainCondemnAuto_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "chain's arena") \
|
||||
PARAM(X, 1, P, chain, "chain with gens being condemned") \
|
||||
PARAM(X, 2, P, trace, "trace for which gens condemned") \
|
||||
PARAM(X, 3, W, topCondemnedGenIndex, "condemned gens [0..this]") \
|
||||
PARAM(X, 4, W, genCount, "total gens in chain")
|
||||
|
||||
#define EVENT_CommitLimitSet_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, W, limit, "new commit limit") \
|
||||
PARAM(X, 2, U, res, "result code")
|
||||
|
||||
#define EVENT_EventClockSync_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, clock, "mps_clock() value")
|
||||
|
||||
#define EVENT_EventInit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, U, major, "EVENT_VERSION_MAJOR") \
|
||||
PARAM(X, 1, U, median, "EVENT_VERSION_MEDIAN") \
|
||||
PARAM(X, 2, U, minor, "EVENT_VERSION_MINOR") \
|
||||
PARAM(X, 3, U, maxCode, "EventCodeMAX") \
|
||||
PARAM(X, 4, U, maxNameLen, "EventNameMAX") \
|
||||
PARAM(X, 5, U, wordWidth, "MPS_WORD_WIDTH") \
|
||||
PARAM(X, 6, W, clocksPerSec, "mps_clocks_per_sec()")
|
||||
|
||||
#define EVENT_GenFinish_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "generation's arena") \
|
||||
PARAM(X, 1, P, gen, "the generation") \
|
||||
PARAM(X, 2, U, serial, "serial number within arena")
|
||||
|
||||
#define EVENT_GenInit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "generation's arena") \
|
||||
PARAM(X, 1, P, gen, "the generation") \
|
||||
PARAM(X, 2, U, serial, "serial number within arena") \
|
||||
PARAM(X, 3, W, capacity, "capacity in bytes") \
|
||||
PARAM(X, 4, D, mortality, "initial mortality estimate")
|
||||
|
||||
#define EVENT_GenZoneSet_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "generation's arena") \
|
||||
PARAM(X, 1, P, gen, "the generation") \
|
||||
PARAM(X, 2, W, zoneSet, "generation's new summary")
|
||||
|
||||
#define EVENT_Intern_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, stringId, "identifier of interned string") \
|
||||
PARAM(X, 1, S, string, "the interned string")
|
||||
|
||||
#define EVENT_Label_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, A, address, "address") \
|
||||
PARAM(X, 1, W, stringId, "string identifier of its label")
|
||||
|
||||
#define EVENT_LabelPointer_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pointer, "pointer") \
|
||||
PARAM(X, 1, W, stringId, "string identifier of its label")
|
||||
|
||||
#define EVENT_LandInit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, land, "the land") \
|
||||
PARAM(X, 1, P, owner, "owner pointer")
|
||||
|
||||
#define EVENT_MessagesDropped_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, count, "count of messages dropped")
|
||||
|
||||
#define EVENT_MessagesExist_PARAMS(PARAM, X)
|
||||
|
||||
#define EVENT_MeterInit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, meter, "the meter") \
|
||||
PARAM(X, 1, P, owner, "owner pointer")
|
||||
|
||||
#define EVENT_MeterValues_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, meter, "the meter") \
|
||||
PARAM(X, 1, D, total, "sum of metered amounts") \
|
||||
PARAM(X, 2, D, meanSquared, "mean square of metered amounts") \
|
||||
PARAM(X, 3, W, count, "number of metered amounts") \
|
||||
PARAM(X, 4, W, max, "maximum metered amount") \
|
||||
PARAM(X, 5, W, min, "minimum metered amount")
|
||||
|
||||
#define EVENT_PauseTimeSet_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, D, pauseTime, "the new maximum pause time, in seconds")
|
||||
|
||||
#define EVENT_PoolAlloc_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, A, pReturn, "base of allocated memory") \
|
||||
PARAM(X, 2, W, size, "size of client request")
|
||||
|
||||
#define EVENT_PoolFinish_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, arena, "pool's arena")
|
||||
|
||||
#define EVENT_PoolFree_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, A, old, "base of freed memory") \
|
||||
PARAM(X, 2, W, size, "size of client request")
|
||||
|
||||
#define EVENT_PoolInit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, arena, "pool's arena") \
|
||||
PARAM(X, 2, P, poolClass, "pool's class") \
|
||||
PARAM(X, 3, U, serial, "pool's serial number within the arena")
|
||||
|
||||
#define EVENT_PoolInitAMCZ_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, format, "pool's format")
|
||||
|
||||
#define EVENT_PoolInitAMC_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, format, "pool's format")
|
||||
|
||||
#define EVENT_PoolInitAMS_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, format, "pool's format")
|
||||
|
||||
#define EVENT_PoolInitAWL_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, format, "pool's format")
|
||||
|
||||
#define EVENT_PoolInitLO_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, format, "pool's format")
|
||||
|
||||
#define EVENT_PoolInitMFS_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, W, extendBy, "size of extents") \
|
||||
PARAM(X, 2, B, extendSelf, "automatically extend?") \
|
||||
PARAM(X, 3, W, unitSize, "size of allocations")
|
||||
|
||||
#define EVENT_PoolInitMVFF_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, W, extendBy, "size of extents") \
|
||||
PARAM(X, 2, W, avgSize, "predicted mean size of blocks") \
|
||||
PARAM(X, 3, W, align, "alignment of blocks") \
|
||||
PARAM(X, 4, B, slotHigh, "allocate at high addresses within extents?") \
|
||||
PARAM(X, 5, B, arenaHigh, "allocate extents at high addresses?") \
|
||||
PARAM(X, 6, B, firstFit, "allocate from first free block?")
|
||||
|
||||
#define EVENT_PoolInitMVT_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, W, minSize, "predicted minimum size of blocks") \
|
||||
PARAM(X, 2, W, meanSize, "predicted mean size of blocks") \
|
||||
PARAM(X, 3, W, maxSize, "predicted maximum size of blocks") \
|
||||
PARAM(X, 4, W, reserveDepth, "reserve space for this many allocations") \
|
||||
PARAM(X, 5, W, fragLimig, "fragmentation limit")
|
||||
|
||||
#define EVENT_PoolInitSNC_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, pool, "the pool") \
|
||||
PARAM(X, 1, P, format, "pool's format")
|
||||
|
||||
#define EVENT_RootScan_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, root, "the root") \
|
||||
PARAM(X, 1, W, ts, "scanning for this set of traces") \
|
||||
PARAM(X, 2, W, summary, "summary after scan")
|
||||
|
||||
#define EVENT_SegAlloc_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, P, seg, "new segment") \
|
||||
PARAM(X, 2, A, base, "base address") \
|
||||
PARAM(X, 3, W, size, "segment size") \
|
||||
PARAM(X, 4, P, pool, "pool making request")
|
||||
|
||||
#define EVENT_SegAllocFail_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, W, size, "requested segment size") \
|
||||
PARAM(X, 2, P, pool, "pool making request") \
|
||||
PARAM(X, 3, U, res, "result code")
|
||||
|
||||
#define EVENT_SegFree_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "segment's arena") \
|
||||
PARAM(X, 1, P, seg, "the segment")
|
||||
|
||||
#define EVENT_SegMerge_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, segLo, "low segment") \
|
||||
PARAM(X, 1, P, segHi, "high segment")
|
||||
|
||||
#define EVENT_SegReclaim_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, seg, "the segment") \
|
||||
PARAM(X, 1, P, pool, "segment's pool") \
|
||||
PARAM(X, 2, P, arena, "pool's arena") \
|
||||
PARAM(X, 3, P, trace, "reclaiming for this trace")
|
||||
|
||||
#define EVENT_SegScan_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, seg, "the segment") \
|
||||
PARAM(X, 1, P, pool, "segment's pool") \
|
||||
PARAM(X, 2, P, arena, "pool's arena") \
|
||||
PARAM(X, 3, U, ts, "scanning for this set of traces") \
|
||||
PARAM(X, 4, U, rank, "scanning at this rank")
|
||||
|
||||
#define EVENT_SegSetGrey_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "segment's arena") \
|
||||
PARAM(X, 1, P, seg, "the segment") \
|
||||
PARAM(X, 2, U, grey, "greyen for this set of traces")
|
||||
|
||||
#define EVENT_SegSetSummary_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "segment's arena") \
|
||||
PARAM(X, 1, P, seg, "the segment") \
|
||||
PARAM(X, 2, W, size, "its size in bytes") \
|
||||
PARAM(X, 3, W, oldSummary, "old summary") \
|
||||
PARAM(X, 4, W, newSummary, "new summary")
|
||||
|
||||
#define EVENT_SegSplit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, seg, "old segment") \
|
||||
PARAM(X, 1, P, segLo, "new low segment") \
|
||||
PARAM(X, 2, P, segHi, "new high segment") \
|
||||
PARAM(X, 3, A, at, "split address")
|
||||
|
||||
#define EVENT_TraceAccess_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, P, seg, "segment accessed") \
|
||||
PARAM(X, 2, U, mode, "set of access modes")
|
||||
|
||||
#define EVENT_TraceBandAdvance_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, P, trace, "the trace") \
|
||||
PARAM(X, 2, W, rank, "new rank")
|
||||
|
||||
#define EVENT_TraceCondemnAll_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "trace's arena") \
|
||||
PARAM(X, 1, P, trace, "trace")
|
||||
|
||||
#define EVENT_TraceCreate_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, trace, "trace that was created") \
|
||||
PARAM(X, 1, P, arena, "arena in which created") \
|
||||
PARAM(X, 2, U, why, "reason for creation")
|
||||
|
||||
#define EVENT_TraceCreatePoolGen_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, gen, "generation") \
|
||||
PARAM(X, 1, W, capacity, "capacity of generation") \
|
||||
PARAM(X, 2, D, mortality, "mortality of generation") \
|
||||
PARAM(X, 3, W, zone, "zone set of generation") \
|
||||
PARAM(X, 4, P, pool, "pool") \
|
||||
PARAM(X, 5, W, totalSize, "total size of pool gen") \
|
||||
PARAM(X, 6, W, freeSize, "free size of pool gen") \
|
||||
PARAM(X, 7, W, newSize, "new size of pool gen") \
|
||||
PARAM(X, 8, W, oldSize, "old size of pool gen") \
|
||||
PARAM(X, 9, W, newDeferredSize, "new size (deferred) of pool gen") \
|
||||
PARAM(X, 10, W, oldDeferredSize, "old size (deferred) of pool gen")
|
||||
|
||||
#define EVENT_TraceDestroy_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "trace's arena") \
|
||||
PARAM(X, 1, P, trace, "the trace")
|
||||
|
||||
#define EVENT_TraceEndGen_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "the arena") \
|
||||
PARAM(X, 1, P, trace, "the trace") \
|
||||
PARAM(X, 2, P, gen, "the generation") \
|
||||
PARAM(X, 3, W, condemned, "bytes condemned in generation") \
|
||||
PARAM(X, 4, W, forwarded, "bytes forwarded from generation") \
|
||||
PARAM(X, 5, W, preservedInPlace, "bytes preserved in generation") \
|
||||
PARAM(X, 6, D, mortalityTrace, "mortality (in last trace only)") \
|
||||
PARAM(X, 7, D, mortalityAverage, "mortality (moving average)")
|
||||
|
||||
#define EVENT_TraceFindGrey_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "trace's arena") \
|
||||
PARAM(X, 1, P, trace, "the trace") \
|
||||
PARAM(X, 2, P, seg, "grey segment found") \
|
||||
PARAM(X, 3, W, rank, "current rank")
|
||||
|
||||
#define EVENT_TraceFix_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, ss, "the scan state") \
|
||||
PARAM(X, 1, P, refIO, "pointer to reference") \
|
||||
PARAM(X, 2, A, ref, "reference fixed") \
|
||||
PARAM(X, 3, U, rank, "current rank")
|
||||
|
||||
#define EVENT_TraceFixSeg_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, seg, "the segment")
|
||||
|
||||
#define EVENT_TraceFlipBegin_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, trace, "the trace") \
|
||||
PARAM(X, 1, P, arena, "trace's arena")
|
||||
|
||||
#define EVENT_TraceFlipEnd_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, trace, "the trace") \
|
||||
PARAM(X, 1, P, arena, "trace's arena")
|
||||
|
||||
#define EVENT_TraceReclaim_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, trace, "the trace") \
|
||||
PARAM(X, 1, P, arena, "trace's arena")
|
||||
|
||||
#define EVENT_TraceScanArea_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, ss, "the scan state") \
|
||||
PARAM(X, 1, P, base, "base of scanned area") \
|
||||
PARAM(X, 2, P, limit, "limit of scanned area")
|
||||
|
||||
#define EVENT_TraceScanAreaTagged_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, ss, "the scan state") \
|
||||
PARAM(X, 1, P, base, "base of scanned area") \
|
||||
PARAM(X, 2, P, limit, "limit of scanned area")
|
||||
|
||||
#define EVENT_TraceScanSingleRef_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, U, ts, "set of traces") \
|
||||
PARAM(X, 1, U, rank, "current rank") \
|
||||
PARAM(X, 2, P, arena, "traces' arena") \
|
||||
PARAM(X, 3, P, refIO, "pointer to reference")
|
||||
|
||||
#define EVENT_TraceStart_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, arena, "trace's arena") \
|
||||
PARAM(X, 1, P, trace, "trace being started") \
|
||||
PARAM(X, 2, D, mortality, "as passed to TraceStart") \
|
||||
PARAM(X, 3, D, finishingTime, "as passed to TraceStart") \
|
||||
PARAM(X, 4, W, condemned, "condemned bytes") \
|
||||
PARAM(X, 5, W, notCondemned, "collectible but not condemned bytes") \
|
||||
PARAM(X, 6, W, foundation, "foundation size") \
|
||||
PARAM(X, 7, W, white, "white reference set") \
|
||||
PARAM(X, 8, W, quantumWork, "tracing work to be done in each poll")
|
||||
|
||||
#define EVENT_TraceStatFix_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, trace, "the trace") \
|
||||
PARAM(X, 1, P, arena, "trace's arena") \
|
||||
PARAM(X, 2, W, fixRefCount, "references which pass zone check") \
|
||||
PARAM(X, 3, W, segRefCount, "references which refer to segments") \
|
||||
PARAM(X, 4, W, whiteSegRefCount, "references which refer to white segments") \
|
||||
PARAM(X, 5, W, nailCount, "segments nailed by ambiguous references") \
|
||||
PARAM(X, 6, W, snapCount, "references snapped to forwarded objects") \
|
||||
PARAM(X, 7, W, forwardedCount, "objects preserved by moving") \
|
||||
PARAM(X, 8, W, forwardedSize, "bytes preserved by moving") \
|
||||
PARAM(X, 9, W, preservedInPlaceCount, "objects preserved in place") \
|
||||
PARAM(X, 10, W, preservedInPlaceSize, "bytes preserved in place")
|
||||
|
||||
#define EVENT_TraceStatReclaim_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, trace, "the trace") \
|
||||
PARAM(X, 1, P, arena, "trace's arena") \
|
||||
PARAM(X, 2, W, reclaimCount, "segments reclaimed") \
|
||||
PARAM(X, 3, W, reclaimSize, "bytes reclaimed")
|
||||
|
||||
#define EVENT_TraceStatScan_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, trace, "the trace") \
|
||||
PARAM(X, 1, P, arena, "trace's arena") \
|
||||
PARAM(X, 2, W, rootScanCount, "number of roots scanned") \
|
||||
PARAM(X, 3, W, rootScanSize, "total size of scanned roots") \
|
||||
PARAM(X, 4, W, rootCopiedSize, "bytes copied by scanning roots ") \
|
||||
PARAM(X, 5, W, segScanCount, "number of segments scanned") \
|
||||
PARAM(X, 6, W, segScanSize, "total size of scanned segments") \
|
||||
PARAM(X, 7, W, segCopiedSize, "bytes copied by scanning segments") \
|
||||
PARAM(X, 8, W, singleScanCount, "number of single references scanned") \
|
||||
PARAM(X, 9, W, singleScanSize, "total size of single references scanned") \
|
||||
PARAM(X, 10, W, singleCopiedSize, "bytes copied by scanning single references") \
|
||||
PARAM(X, 11, W, readBarrierHitCount, "read barrier faults") \
|
||||
PARAM(X, 12, W, greySegMax, "maximum number of grey segments") \
|
||||
PARAM(X, 13, W, pointlessScanCount, "pointless segment scans")
|
||||
|
||||
#define EVENT_VMArenaExtendDone_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, chunkSize, "request succeeded for chunkSize bytes") \
|
||||
PARAM(X, 1, W, reserved, "new VMArenaReserved")
|
||||
|
||||
#define EVENT_VMArenaExtendFail_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, chunkMin, "no remaining address space chunk >= chunkMin") \
|
||||
PARAM(X, 1, W, reserved, "current VMArenaReserved")
|
||||
|
||||
#define EVENT_VMArenaExtendStart_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, size, "size to accommodate") \
|
||||
PARAM(X, 1, W, chunkSize, "chunkSize to try") \
|
||||
PARAM(X, 2, W, reserved, "current VMArenaReserved")
|
||||
|
||||
#define EVENT_VMCompact_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, W, vmem0, "pre-collection reserved size") \
|
||||
PARAM(X, 1, W, vmem1, "pre-compact reserved size") \
|
||||
PARAM(X, 2, W, vmem2, "post-compact reserved size")
|
||||
|
||||
#define EVENT_VMFinish_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, vm, "the VM")
|
||||
|
||||
#define EVENT_VMInit_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, vm, "the VM") \
|
||||
PARAM(X, 1, A, base, "base of VM") \
|
||||
PARAM(X, 2, A, limit, "limit of VM")
|
||||
|
||||
#define EVENT_VMMap_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, vm, "the VM") \
|
||||
PARAM(X, 1, A, base, "base of mapped addresses") \
|
||||
PARAM(X, 2, A, limit, "limit of mapped addresses")
|
||||
|
||||
#define EVENT_VMUnmap_PARAMS(PARAM, X) \
|
||||
PARAM(X, 0, P, vm, "the VM") \
|
||||
PARAM(X, 1, A, base, "base of unmapped addresses") \
|
||||
PARAM(X, 2, A, limit, "limit of unmapped addresses")
|
||||
|
||||
#endif /* eventdef_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
226
mps/code/eventpy.c
Normal file
226
mps/code/eventpy.c
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
/* eventpy.c: GENERATE PYTHON INTERFACE TO EVENTS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2016-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* This command-line program emits Python data structures that can be
|
||||
* used to parse a telemetry stream in the binary format for the
|
||||
* platform it was compiled for.
|
||||
*/
|
||||
|
||||
#include <assert.h> /* assert */
|
||||
#include <stddef.h> /* offsetof */
|
||||
#include <stdio.h> /* printf, puts */
|
||||
|
||||
#include "event.h"
|
||||
|
||||
/* See <https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment> */
|
||||
#if defined(MPS_ARCH_A6) || defined(MPS_ARCH_I3) || defined(MPS_ARCH_I6)
|
||||
#define BYTE_ORDER "<"
|
||||
#else
|
||||
#error "Can't determine byte order for platform architecture."
|
||||
#endif
|
||||
|
||||
|
||||
/* format -- output struct format code corresponding to event field
|
||||
*
|
||||
* size is the size of the field in bytes
|
||||
* sort is a one-character string corresponding to the EventF* typedef
|
||||
* for the field, thus "P" for a field of type EventFP.
|
||||
*
|
||||
* See <https://docs.python.org/3/library/struct.html>
|
||||
*/
|
||||
|
||||
static void format(size_t size, const char *sort)
|
||||
{
|
||||
switch (sort[0]) {
|
||||
case 'B':
|
||||
printf("?");
|
||||
break;
|
||||
case 'D':
|
||||
printf("d");
|
||||
break;
|
||||
case 'S':
|
||||
/* Strings can't be handled through the struct format mechanism
|
||||
because we don't know their length until the header has been
|
||||
read. */
|
||||
break;
|
||||
default:
|
||||
switch (size) {
|
||||
case 1:
|
||||
printf("B");
|
||||
break;
|
||||
case 2:
|
||||
printf("H");
|
||||
break;
|
||||
case 4:
|
||||
printf("L");
|
||||
break;
|
||||
case 8:
|
||||
printf("Q");
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
size_t size, prev_offset;
|
||||
char prev_sort;
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
|
||||
puts("from collections import namedtuple");
|
||||
|
||||
printf("\n__version__ = %d, %d, %d\n", EVENT_VERSION_MAJOR,
|
||||
EVENT_VERSION_MEDIAN, EVENT_VERSION_MINOR);
|
||||
|
||||
puts("\n# Description of an event kind.");
|
||||
puts("KindDesc = namedtuple('KindDesc', 'name code doc')");
|
||||
|
||||
puts("\n# Namespace containing a KindDesc for every kind.");
|
||||
puts("class Kind:");
|
||||
#define ENUM(X, NAME, DOC) \
|
||||
printf(" " #NAME " = KindDesc('" #NAME "', %d, '%s')\n", \
|
||||
EventKind ## NAME, DOC);
|
||||
EventKindENUM(ENUM, X);
|
||||
#undef ENUM
|
||||
|
||||
puts("\n# Mapping from kind number to KindDesc.");
|
||||
puts("KIND = {");
|
||||
#define ENUM(X, NAME, DOC) \
|
||||
printf(" %d: Kind." #NAME ",\n", EventKind ## NAME);
|
||||
EventKindENUM(ENUM, X);
|
||||
#undef ENUM
|
||||
puts("}");
|
||||
|
||||
puts("\n# Description of a parameter of an event.");
|
||||
puts("EventParam = namedtuple('EventParam', 'sort name doc')");
|
||||
|
||||
puts("\n# Description of the parameters of an event.");
|
||||
puts("EventDesc = namedtuple('EventDesc', "
|
||||
"'name code used kind params maxsize format')");
|
||||
|
||||
puts("\n# Namespace containing an EventDesc for every event.");
|
||||
puts("class Event:");
|
||||
#define PAD_TO(OFFSET) \
|
||||
BEGIN { \
|
||||
size_t offset = (OFFSET); \
|
||||
if (prev_sort != 'S' && prev_offset < offset) \
|
||||
printf("%ux", (unsigned)(offset - prev_offset)); \
|
||||
prev_offset = offset; \
|
||||
} END
|
||||
#define EVENT_PARAM(X, INDEX, SORT, IDENT, DOC) \
|
||||
puts(" EventParam('" #SORT "', '" #IDENT "', \"" DOC "\"),"); \
|
||||
prev_sort = #SORT[0];
|
||||
#define EVENT_FORMAT(NAME, INDEX, SORT, IDENT, DOC) \
|
||||
PAD_TO(offsetof(Event##NAME##Struct, f##INDEX)); \
|
||||
format(sizeof(EventF##SORT), #SORT); \
|
||||
prev_offset += sizeof(EventF##SORT);
|
||||
#define EVENT_DEFINE(X, NAME, CODE, USED, KIND) \
|
||||
printf(" " #NAME " = EventDesc('" #NAME "', %d, %s, Kind." #KIND ", [\n", \
|
||||
CODE, USED ? "True" : "False"); \
|
||||
EVENT_ ## NAME ## _PARAMS(EVENT_PARAM, X); \
|
||||
size = sizeof(Event##NAME##Struct) - sizeof(EventAnyStruct); \
|
||||
printf(" ], %u, '%s", (unsigned)size, BYTE_ORDER); \
|
||||
prev_offset = sizeof(EventAnyStruct); \
|
||||
EVENT_ ## NAME ## _PARAMS(EVENT_FORMAT, NAME); \
|
||||
PAD_TO(sizeof(Event##NAME##Struct)); \
|
||||
puts("')");
|
||||
EVENT_LIST(EVENT_DEFINE, 0);
|
||||
#undef EVENT_DEFINE
|
||||
#undef EVENT_PARAM
|
||||
#undef EVENT_FORMAT
|
||||
|
||||
puts("\n# Mapping from event number to EventDesc.");
|
||||
puts("EVENT = {");
|
||||
#define EVENT_ITEM(X, NAME, CODE, USED, KIND) \
|
||||
printf(" %d: Event." #NAME ",\n", CODE);
|
||||
EVENT_LIST(EVENT_ITEM, 0);
|
||||
#undef EVENT_ITEM
|
||||
puts("}");
|
||||
|
||||
puts("\n# Description of an event header.");
|
||||
printf("HeaderDesc = namedtuple('HeaderDesc', '");
|
||||
#define EVENT_FIELD(TYPE, NAME, DOC) printf("%s ", #NAME);
|
||||
EVENT_ANY_FIELDS(EVENT_FIELD)
|
||||
#undef EVENT_FIELD
|
||||
puts("')\nHeaderDesc.__doc__ = '''");
|
||||
#define EVENT_FIELD(TYPE, NAME, DOC) printf("%s -- %s\n", #NAME, DOC);
|
||||
EVENT_ANY_FIELDS(EVENT_FIELD)
|
||||
#undef EVENT_FIELD
|
||||
puts("'''");
|
||||
|
||||
puts("\n# Size of event header in bytes.");
|
||||
printf("HEADER_SIZE = %u\n", (unsigned)sizeof(EventAnyStruct));
|
||||
|
||||
puts("\n# Struct format for event header.");
|
||||
printf("HEADER_FORMAT = '%s", BYTE_ORDER);
|
||||
prev_offset = 0;
|
||||
#define EVENT_FIELD(TYPE, NAME, DOC) \
|
||||
PAD_TO(offsetof(EventAnyStruct, NAME)); \
|
||||
format(sizeof(TYPE), "?"); \
|
||||
prev_offset += sizeof(TYPE);
|
||||
EVENT_ANY_FIELDS(EVENT_FIELD)
|
||||
#undef EVENT_FIELD
|
||||
PAD_TO(sizeof(EventAnyStruct));
|
||||
puts("'");
|
||||
|
||||
puts("\n# Mapping from access mode to its name.");
|
||||
puts("ACCESS_MODE = {");
|
||||
printf(" %u: \"READ\",\n", (unsigned)AccessREAD);
|
||||
printf(" %u: \"WRITE\",\n", (unsigned)AccessWRITE);
|
||||
printf(" %u: \"READ/WRITE\",\n",
|
||||
(unsigned)BS_UNION(AccessREAD, AccessWRITE));
|
||||
puts("}");
|
||||
|
||||
puts("\n# Mapping from rank to its name.");
|
||||
puts("RANK = {");
|
||||
#define X(RANK) printf(" %u: \"%s\",\n", (unsigned)Rank ## RANK, #RANK);
|
||||
RANK_LIST(X)
|
||||
#undef X
|
||||
puts("}");
|
||||
|
||||
puts("\n# Mapping from trace start reason to its short description.");
|
||||
puts("TRACE_START_WHY = {");
|
||||
#define X(WHY, SHORT, LONG) \
|
||||
printf(" %u: \"%s\",\n", (unsigned)TraceStartWhy ## WHY, SHORT);
|
||||
TRACE_START_WHY_LIST(X)
|
||||
#undef X
|
||||
puts("}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2016-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
997
mps/code/eventsql.c
Normal file
997
mps/code/eventsql.c
Normal file
|
|
@ -0,0 +1,997 @@
|
|||
/* eventsql.c: event log to SQLite importer.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2012-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* This is a command-line tool that imports events from a text-format
|
||||
* MPS telemetry file into a SQLite database file.
|
||||
*
|
||||
* The default MPS library will write a binary-format telemetry file
|
||||
* which can be converted into a text-format file using the eventcnv
|
||||
* program (q.v.).
|
||||
*
|
||||
* Each event type gets its own table in the database. These tables
|
||||
* are created from the definitions in eventdef.h if they don't
|
||||
* already exist. Each event becomes a single row in the appropriate
|
||||
* table, which has a column for each event parameter, a time column
|
||||
* for the event time field, and a log_serial column to identify the
|
||||
* source log file. Because the database schema depends on the event
|
||||
* definitions in eventdef.h, eventsql has to be compiled using the
|
||||
* same event header files as those used to compile the MPS and
|
||||
* eventcnv which generated and processed the telemetry output.
|
||||
*
|
||||
* The program also creates several other tables: three 'glue' tables
|
||||
* containing event metadata - event_kind (one row per kind),
|
||||
* event_type (one row per type), and event_param (one row per
|
||||
* parameter), all derived from eventdef.h - and the event_log table
|
||||
* which has one row per log file imported (the log_serial column in
|
||||
* the event tables is a primary key to this event_log table).
|
||||
*
|
||||
* No tables are created if they already exist, unless the -r
|
||||
* (rebuild) switch is given.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* -v (verbose): Increase verbosity. eventsql logs to stderr. By
|
||||
* default, it doesn't log much; it can be made more and more
|
||||
* loquacious by adding more -v switches.
|
||||
*
|
||||
* -p (progress): Show progress with a series of dots written to
|
||||
* standard output (one dot per 100,000 events processed). Defaults
|
||||
* on if -v specified, off otherwise.
|
||||
*
|
||||
* -t (test): Run unit tests on parts of eventsql. There aren't many
|
||||
* of these. TODO: write more unit tests.
|
||||
*
|
||||
* -d (delete): Delete the SQL file before importing.
|
||||
*
|
||||
* -f (force): Import the events to SQL even if the SQL database
|
||||
* already includes a record of importing a matching log file.
|
||||
*
|
||||
* -r (rebuild): Drop the glue tables from SQL, which will force them
|
||||
* to be recreated. Important if you change event types or kinds in
|
||||
* eventdef.h.
|
||||
*
|
||||
* -i <logfile>: Import events from the named logfile. Defaults to
|
||||
* standard input. If the specified file (matched by size and
|
||||
* modtime) has previously been imported to the same database, it will
|
||||
* not be imported again unless -f is specified.
|
||||
*
|
||||
* -o <database>: Import events to the named database file. If not
|
||||
* specified, eventsql will use the MPS_TELEMETRY_DATABASE environment
|
||||
* variable, and default to "mpsevent.db".
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include "misc.h"
|
||||
#include "config.h"
|
||||
#include "eventdef.h"
|
||||
#include "eventcom.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* on Windows, we build SQLite locally from the amalgamated sources */
|
||||
#ifdef MPS_BUILD_MV
|
||||
#include "sqlite3.h"
|
||||
#else
|
||||
#include <sqlite3.h>
|
||||
#endif
|
||||
|
||||
#define DATABASE_NAME_ENVAR "MPS_TELEMETRY_DATABASE"
|
||||
#define DEFAULT_DATABASE_NAME "mpsevent.db"
|
||||
|
||||
#ifdef MPS_BUILD_MV
|
||||
#define strtoll _strtoi64
|
||||
#endif
|
||||
|
||||
|
||||
typedef sqlite3_int64 int64;
|
||||
|
||||
/* At non-zero verbosity levels we output rows of dots. One dot per
|
||||
* SMALL_TICK events, BIG_TICK dots per row. */
|
||||
|
||||
#define SMALL_TICK 100000
|
||||
#define BIG_TICK 50
|
||||
|
||||
/* Utility code for logging to stderr with multiple log levels,
|
||||
* and for reporting errors.
|
||||
*/
|
||||
|
||||
static unsigned int verbosity = 0;
|
||||
|
||||
#define LOG_ALWAYS 0
|
||||
#define LOG_OFTEN 1
|
||||
#define LOG_SOMETIMES 2
|
||||
#define LOG_SELDOM 3
|
||||
#define LOG_RARELY 4
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 2, 0))
|
||||
static void vlog(unsigned int level, const char *format, va_list args)
|
||||
{
|
||||
if (level <= verbosity) {
|
||||
fflush(stderr); /* sync */
|
||||
fprintf(stderr, "log %d: ", level);
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 2, 3))
|
||||
static void evlog(unsigned int level, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vlog(level, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 1, 2))
|
||||
static void error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
fprintf(stderr, "Fatal error: ");
|
||||
va_start(args, format);
|
||||
vlog(LOG_ALWAYS, format, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void sqlite_error(int res, sqlite3 *db, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
evlog(LOG_ALWAYS, "Fatal SQL error %d", res);
|
||||
va_start(args, format);
|
||||
vlog(LOG_ALWAYS, format, args);
|
||||
va_end(args);
|
||||
evlog(LOG_ALWAYS, "SQLite message: %s\n", sqlite3_errmsg(db));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* global control variables set by command-line parameters. */
|
||||
|
||||
static const char *prog; /* program name */
|
||||
static int rebuild = FALSE;
|
||||
static int deleteDatabase = FALSE;
|
||||
static int runTests = FALSE;
|
||||
static int force = FALSE;
|
||||
static int progress = FALSE;
|
||||
static const char *databaseName = NULL;
|
||||
static const char *logFileName = NULL;
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [-rfdvt] [-i <logfile>] [-o <database>]\n"
|
||||
" -h (help) : this message.\n"
|
||||
" -r (rebuild) : re-create glue tables.\n"
|
||||
" -f (force) : ignore previous import of same logfile.\n"
|
||||
" -d (delete) : delete and recreate database file.\n"
|
||||
" -v (verbose) : increase logging to stderr.\n"
|
||||
" -p (progress): show progress with dots to stdout.\n"
|
||||
" -t (test) : run self-tests.\n"
|
||||
" -i <logfile> : read logfile (defaults to stdin)\n"
|
||||
" -o <database>: write database (defaults to\n"
|
||||
" "
|
||||
DATABASE_NAME_ENVAR " or " DEFAULT_DATABASE_NAME ").\n",
|
||||
prog);
|
||||
}
|
||||
|
||||
static void usageError(void)
|
||||
{
|
||||
usage();
|
||||
error("Bad usage");
|
||||
}
|
||||
|
||||
/* parseArgs -- parse command line arguments */
|
||||
|
||||
static void parseArgs(int argc, char *argv[])
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
if (argc >= 1)
|
||||
prog = argv[0];
|
||||
else
|
||||
prog = "unknown";
|
||||
|
||||
while(i < argc) { /* consider argument i */
|
||||
if (argv[i][0] == '-') { /* it's an option argument */
|
||||
char *p = argv[i] + 1;
|
||||
while(*p) {
|
||||
switch (*p) {
|
||||
case 'v': /* verbosity */
|
||||
++ verbosity;
|
||||
break;
|
||||
case 'p': /* progress */
|
||||
progress = TRUE;
|
||||
break;
|
||||
case 'r': /* rebuild */
|
||||
rebuild = TRUE;
|
||||
break;
|
||||
case 'd': /* rebuild */
|
||||
deleteDatabase = TRUE;
|
||||
break;
|
||||
case 'f': /* force */
|
||||
force = TRUE;
|
||||
break;
|
||||
case 't': /* run tests */
|
||||
runTests = TRUE;
|
||||
break;
|
||||
case 'i': /* input (log file) name */
|
||||
if (p[1] == '\0') { /* last character in this arg; name is next arg */
|
||||
logFileName = argv[i+1];
|
||||
++ i;
|
||||
} else { /* not last character in arg; name is rest of arg */
|
||||
logFileName = p+1;
|
||||
}
|
||||
goto next_i;
|
||||
case 'o': /* output (database file) name */
|
||||
if (p[1] == '\0') { /* last character in this arg; name is next arg */
|
||||
databaseName = argv[i+1];
|
||||
++ i;
|
||||
} else { /* not last character in arg; name is rest of arg */
|
||||
databaseName = p+1;
|
||||
}
|
||||
goto next_i;
|
||||
case 'h':
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
usageError();
|
||||
}
|
||||
++ p;
|
||||
}
|
||||
} else { /* not an option argument */
|
||||
usageError();
|
||||
}
|
||||
next_i:
|
||||
++ i;
|
||||
}
|
||||
if (verbosity > LOG_ALWAYS)
|
||||
progress = TRUE;
|
||||
}
|
||||
|
||||
/* openDatabase(p) opens the database file and returns a SQLite 3
|
||||
* database connection object. */
|
||||
|
||||
static sqlite3 *openDatabase(void)
|
||||
{
|
||||
sqlite3 *db;
|
||||
int res;
|
||||
|
||||
if (!databaseName) {
|
||||
databaseName = getenv(DATABASE_NAME_ENVAR);
|
||||
if(!databaseName)
|
||||
databaseName = DEFAULT_DATABASE_NAME;
|
||||
}
|
||||
|
||||
if (deleteDatabase) {
|
||||
res = remove(databaseName);
|
||||
if (res)
|
||||
evlog(LOG_ALWAYS, "Could not remove database file %s", databaseName);
|
||||
else
|
||||
evlog(LOG_OFTEN, "Removed database file %s", databaseName);
|
||||
}
|
||||
|
||||
res = sqlite3_open_v2(databaseName,
|
||||
&db,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
|
||||
NULL); /* use default sqlite_vfs object */
|
||||
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "Opening %s failed", databaseName);
|
||||
|
||||
evlog(LOG_OFTEN, "Writing to %s.",databaseName);
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
/* closeDatabase(db) closes the database opened by openDatabase(). */
|
||||
|
||||
static void closeDatabase(sqlite3 *db)
|
||||
{
|
||||
int res = sqlite3_close(db);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "Closing database failed");
|
||||
evlog(LOG_SOMETIMES, "Closed %s.", databaseName);
|
||||
}
|
||||
|
||||
/* Utility functions for SQLite statements. */
|
||||
|
||||
static sqlite3_stmt *prepareStatement(sqlite3 *db,
|
||||
const char *sql)
|
||||
{
|
||||
int res;
|
||||
sqlite3_stmt *statement;
|
||||
evlog(LOG_SELDOM, "Preparing statement %s", sql);
|
||||
res = sqlite3_prepare_v2(db, sql,
|
||||
-1, /* prepare whole string as statement */
|
||||
&statement,
|
||||
NULL);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "statement preparation failed: %s", sql);
|
||||
return statement;
|
||||
}
|
||||
|
||||
static void finalizeStatement(sqlite3 *db,
|
||||
sqlite3_stmt *statement)
|
||||
{
|
||||
int res;
|
||||
res = sqlite3_finalize(statement);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "statement finalize failed");
|
||||
}
|
||||
|
||||
static void runStatement(sqlite3 *db,
|
||||
const char *sql,
|
||||
const char *description)
|
||||
{
|
||||
int res;
|
||||
evlog(LOG_SELDOM, "%s: %s", description, sql);
|
||||
res = sqlite3_exec(db,
|
||||
sql,
|
||||
NULL, /* No callback */
|
||||
NULL, /* No callback closure */
|
||||
NULL); /* error messages handled by sqlite_error */
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "%s failed - statement %s", description, sql);
|
||||
}
|
||||
|
||||
/* Test for the existence of a table using sqlite_master table.
|
||||
*/
|
||||
|
||||
static int tableExists(sqlite3* db, const char *tableName)
|
||||
{
|
||||
int res;
|
||||
int exists = 0;
|
||||
sqlite3_stmt *statement = NULL;
|
||||
|
||||
statement = prepareStatement(db,
|
||||
"SELECT 1 FROM sqlite_master WHERE type='table' AND name=?");
|
||||
res = sqlite3_bind_text(statement, 1, tableName, -1, SQLITE_STATIC);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "table existence bind of name failed.");
|
||||
res = sqlite3_step(statement);
|
||||
switch(res) {
|
||||
case SQLITE_DONE:
|
||||
exists = 0;
|
||||
break;
|
||||
case SQLITE_ROW:
|
||||
exists = 1;
|
||||
break;
|
||||
default:
|
||||
sqlite_error(res, db, "select from sqlite_master failed.");
|
||||
}
|
||||
finalizeStatement(db, statement);
|
||||
return exists;
|
||||
}
|
||||
|
||||
/* Unit test for tableExists() */
|
||||
|
||||
static struct {
|
||||
const char* name;
|
||||
int exists;
|
||||
} tableTests[] = {
|
||||
{"event_kind", TRUE},
|
||||
{"spong", FALSE},
|
||||
{"EVENT_SegSplit", TRUE}
|
||||
};
|
||||
|
||||
static void testTableExists(sqlite3 *db)
|
||||
{
|
||||
size_t i;
|
||||
int defects = 0;
|
||||
int tests = 0;
|
||||
for (i=0; i < NELEMS(tableTests); ++i) {
|
||||
const char *name = tableTests[i].name;
|
||||
int exists = tableExists(db, name);
|
||||
if (exists)
|
||||
evlog(LOG_OFTEN, "Table exists: %s", name);
|
||||
else
|
||||
evlog(LOG_OFTEN, "Table does not exist: %s", name);
|
||||
if (exists != tableTests[i].exists) {
|
||||
evlog(LOG_ALWAYS, "tableExists test failed on table %s", name);
|
||||
++ defects;
|
||||
}
|
||||
++ tests;
|
||||
}
|
||||
evlog(LOG_ALWAYS, "%d tests, %d defects found.", tests, defects);
|
||||
}
|
||||
|
||||
/* Every time we put events from a log file into a database file, we
|
||||
* add the log file to the event_log table, and get a serial number
|
||||
* from SQL which is then attached to all event rows from that log.
|
||||
* We use this to record overall SQL activity, to deter mistaken
|
||||
* attempts to add the same log file twice, and to allow events from
|
||||
* several different log files to share the same SQL file.
|
||||
*
|
||||
* When reading events from stdin, we can't so easily avoid the
|
||||
* duplication (unless we, e.g., take a hash of the event set); we
|
||||
* have to assume that the user is smart enough not to do that.
|
||||
*/
|
||||
|
||||
static int64 logSerial = 0;
|
||||
|
||||
static void registerLogFile(sqlite3 *db,
|
||||
const char *filename)
|
||||
{
|
||||
sqlite3_stmt *statement;
|
||||
int res;
|
||||
const unsigned char *name;
|
||||
int64 completed;
|
||||
int64 file_size;
|
||||
int64 file_modtime;
|
||||
|
||||
if (filename) {
|
||||
struct stat st;
|
||||
res = stat(filename, &st);
|
||||
if (res != 0)
|
||||
error("Couldn't stat() %s", filename);
|
||||
file_size = st.st_size;
|
||||
file_modtime = st.st_mtime;
|
||||
|
||||
statement = prepareStatement(db,
|
||||
"SELECT name, serial, completed FROM event_log"
|
||||
" WHERE size = ? AND modtime = ?");
|
||||
res = sqlite3_bind_int64(statement, 1, file_size);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event_log bind of size failed.");
|
||||
res = sqlite3_bind_int64(statement, 2, file_modtime);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event_log bind of modtime failed.");
|
||||
res = sqlite3_step(statement);
|
||||
switch(res) {
|
||||
case SQLITE_DONE:
|
||||
evlog(LOG_SOMETIMES, "No log file matching '%s' found in database.", filename);
|
||||
break;
|
||||
case SQLITE_ROW:
|
||||
name = sqlite3_column_text(statement, 0);
|
||||
logSerial = sqlite3_column_int64(statement, 1);
|
||||
completed = sqlite3_column_int64(statement, 2);
|
||||
evlog(force ? LOG_OFTEN : LOG_ALWAYS, "Log file matching '%s' already in event_log, named \"%s\" (serial %llu, completed %llu).",
|
||||
filename, name, logSerial, completed);
|
||||
if (force) {
|
||||
evlog(LOG_OFTEN, "Continuing anyway because -f specified.");
|
||||
} else {
|
||||
evlog(LOG_ALWAYS, "Exiting. Specify -f to force events into SQL anyway.");
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sqlite_error(res, db, "select from event_log failed.");
|
||||
}
|
||||
finalizeStatement(db, statement);
|
||||
} else { /* stdin */
|
||||
filename = "<stdin>";
|
||||
file_size = 0;
|
||||
file_modtime = 0;
|
||||
}
|
||||
statement = prepareStatement(db,
|
||||
"INSERT into event_log (name, size, modtime, completed)"
|
||||
" VALUES (?, ?, ?, 0)");
|
||||
res = sqlite3_bind_text(statement, 1, filename, -1, SQLITE_STATIC);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event_log insert bind of name failed.");
|
||||
res = sqlite3_bind_int64(statement, 2, file_size);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event_log insert bind of size failed.");
|
||||
res = sqlite3_bind_int64(statement, 3, file_modtime);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event_log insert bind of modtime failed.");
|
||||
res = sqlite3_step(statement);
|
||||
if (res != SQLITE_DONE)
|
||||
sqlite_error(res, db, "insert into event_log failed.");
|
||||
logSerial = sqlite3_last_insert_rowid(db);
|
||||
evlog(LOG_SOMETIMES, "Log file %s added to event_log with serial %llu",
|
||||
filename, logSerial);
|
||||
finalizeStatement(db, statement);
|
||||
}
|
||||
|
||||
static void logFileCompleted(sqlite3 *db,
|
||||
int64 completed)
|
||||
{
|
||||
sqlite3_stmt *statement;
|
||||
int res;
|
||||
|
||||
statement = prepareStatement(db,
|
||||
"UPDATE event_log SET completed=? WHERE serial=?");
|
||||
res = sqlite3_bind_int64(statement, 2, logSerial);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event_log update bind of serial failed.");
|
||||
res = sqlite3_bind_int64(statement, 1, completed);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event_log update bind of completed failed.");
|
||||
res = sqlite3_step(statement);
|
||||
if (res != SQLITE_DONE)
|
||||
sqlite_error(res, db, "insert into event_log failed.");
|
||||
evlog(LOG_SOMETIMES, "Marked in event_log: %llu events", completed);
|
||||
finalizeStatement(db, statement);
|
||||
}
|
||||
|
||||
/* Macro magic to make a CREATE TABLE statement for each event type. */
|
||||
|
||||
#define EVENT_PARAM_SQL_TYPE_A "INTEGER"
|
||||
#define EVENT_PARAM_SQL_TYPE_P "INTEGER"
|
||||
#define EVENT_PARAM_SQL_TYPE_U "INTEGER"
|
||||
#define EVENT_PARAM_SQL_TYPE_W "INTEGER"
|
||||
#define EVENT_PARAM_SQL_TYPE_D "REAL "
|
||||
#define EVENT_PARAM_SQL_TYPE_S "TEXT "
|
||||
#define EVENT_PARAM_SQL_TYPE_B "INTEGER"
|
||||
|
||||
#define EVENT_PARAM_SQL_COLUMN(X, index, sort, ident, doc) \
|
||||
"\"" #ident "\" " EVENT_PARAM_SQL_TYPE_##sort ", "
|
||||
|
||||
#define EVENT_TABLE_CREATE(X, name, code, used, kind) \
|
||||
"CREATE TABLE IF NOT EXISTS EVENT_" #name " ( " \
|
||||
EVENT_##name##_PARAMS(EVENT_PARAM_SQL_COLUMN, X) \
|
||||
"time INTEGER, " \
|
||||
"log_serial INTEGER)",
|
||||
|
||||
/* An array of table-creation statement strings. */
|
||||
|
||||
static const char *createStatements[] = {
|
||||
"CREATE TABLE IF NOT EXISTS event_kind (name TEXT,"
|
||||
" description TEXT,"
|
||||
" enum INTEGER PRIMARY KEY)",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS event_type (name TEXT,"
|
||||
" code INTEGER PRIMARY KEY,"
|
||||
" used INTEGER,"
|
||||
" kind INTEGER,"
|
||||
" FOREIGN KEY (kind) REFERENCES event_kind(enum));",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS event_param (type INTEGER,"
|
||||
" param_index INTEGER,"
|
||||
" sort TEXT,"
|
||||
" ident TEXT,"
|
||||
" doc TEXT,"
|
||||
" FOREIGN KEY (type) REFERENCES event_type(code));",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS event_log (name TEXT,"
|
||||
" size INTEGER,"
|
||||
" modtime INTEGER,"
|
||||
" completed INTEGER,"
|
||||
" serial INTEGER PRIMARY KEY AUTOINCREMENT)",
|
||||
|
||||
EVENT_LIST(EVENT_TABLE_CREATE, X)
|
||||
};
|
||||
|
||||
/* makeTables makes all the tables. */
|
||||
|
||||
static void makeTables(sqlite3 *db)
|
||||
{
|
||||
size_t i;
|
||||
evlog(LOG_SOMETIMES, "Creating tables.");
|
||||
|
||||
for (i=0; i < NELEMS(createStatements); ++i) {
|
||||
runStatement(db, createStatements[i], "Table creation");
|
||||
}
|
||||
}
|
||||
|
||||
static const char *glueTables[] = {
|
||||
"event_kind",
|
||||
"event_type",
|
||||
"event_param",
|
||||
};
|
||||
|
||||
static void dropGlueTables(sqlite3 *db)
|
||||
{
|
||||
size_t i;
|
||||
int res;
|
||||
char sql[1024];
|
||||
|
||||
evlog(LOG_ALWAYS, "Dropping glue tables so they are rebuilt.");
|
||||
|
||||
for (i=0; i < NELEMS(glueTables); ++i) {
|
||||
evlog(LOG_SOMETIMES, "Dropping table %s", glueTables[i]);
|
||||
sprintf(sql, "DROP TABLE %s", glueTables[i]);
|
||||
res = sqlite3_exec(db,
|
||||
sql,
|
||||
NULL, /* No callback */
|
||||
NULL, /* No callback closure */
|
||||
NULL); /* error messages handled by sqlite_error */
|
||||
/* Don't check for errors. */
|
||||
(void)res;
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate the metadata "glue" tables event_kind, event_type, and
|
||||
* event_param. */
|
||||
|
||||
#define EVENT_KIND_DO_INSERT(X, name, description) \
|
||||
res = sqlite3_bind_text(statement, 1, #name, -1, SQLITE_STATIC); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_kind bind of name \"" #name "\" failed."); \
|
||||
res = sqlite3_bind_text(statement, 2, description, -1, SQLITE_STATIC); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_kind bind of description \"" description "\" failed."); \
|
||||
res = sqlite3_bind_int(statement, 3, i); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_kind bind of enum %d failed.", i); \
|
||||
++i; \
|
||||
res = sqlite3_step(statement); \
|
||||
if (res != SQLITE_DONE) \
|
||||
sqlite_error(res, db, "event_kind insert of name \"" #name "\" failed."); \
|
||||
if (sqlite3_changes(db) != 0) \
|
||||
evlog(LOG_SOMETIMES, "Insert of event_kind row for \"" #name "\" affected %d rows.", sqlite3_changes(db)); \
|
||||
res = sqlite3_reset(statement); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "Couldn't reset event_kind insert statement.");
|
||||
|
||||
#define EVENT_TYPE_DO_INSERT(X, name, code, used, kind) \
|
||||
res = sqlite3_bind_text(statement, 1, #name, -1, SQLITE_STATIC); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_type bind of name \"" #name "\" failed."); \
|
||||
res = sqlite3_bind_int(statement, 2, code); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_type bind of code %d failed.", code); \
|
||||
res = sqlite3_bind_int(statement, 3, used); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_type bind of used for name \"" #name "\" failed."); \
|
||||
res = sqlite3_bind_int(statement, 4, EventKind##kind); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_type bind of kind for name \"" #name "\" failed."); \
|
||||
res = sqlite3_step(statement); \
|
||||
if (res != SQLITE_DONE) \
|
||||
sqlite_error(res, db, "event_type insert of name \"" #name "\" failed."); \
|
||||
if (sqlite3_changes(db) != 0) \
|
||||
evlog(LOG_SOMETIMES, "Insert of event_type row for \"" #name "\" affected %d rows.", sqlite3_changes(db)); \
|
||||
res = sqlite3_reset(statement); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "Couldn't reset event_type insert statement.");
|
||||
|
||||
#define EVENT_PARAM_DO_INSERT(code, index, sort, ident, doc) \
|
||||
res = sqlite3_bind_int(statement, 1, code); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_param bind of code %d failed.", code); \
|
||||
res = sqlite3_bind_int(statement, 2, index); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_param bind of index %d failed.", index); \
|
||||
res = sqlite3_bind_text(statement, 3, #sort, -1, SQLITE_STATIC); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_type bind of sort \"" #sort "\" failed."); \
|
||||
res = sqlite3_bind_text(statement, 4, #ident, -1, SQLITE_STATIC); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_type bind of ident \"" #ident "\" failed."); \
|
||||
res = sqlite3_bind_text(statement, 5, doc, -1, SQLITE_STATIC); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "event_type bind of doc \"" doc "\" failed."); \
|
||||
res = sqlite3_step(statement); \
|
||||
if (res != SQLITE_DONE) \
|
||||
sqlite_error(res, db, "event_param insert of \"" #ident "\" for code %d failed.", code); \
|
||||
if (sqlite3_changes(db) != 0) \
|
||||
evlog(LOG_SOMETIMES, "Insert of event_param row for code %d, ident \"" #ident "\" affected %d rows.", code, sqlite3_changes(db)); \
|
||||
res = sqlite3_reset(statement); \
|
||||
if (res != SQLITE_OK) \
|
||||
sqlite_error(res, db, "Couldn't reset event_param insert statement.");
|
||||
|
||||
#define EVENT_TYPE_INSERT_PARAMS(X, name, code, used, kind) \
|
||||
EVENT_##name##_PARAMS(EVENT_PARAM_DO_INSERT, code)
|
||||
|
||||
static void fillGlueTables(sqlite3 *db)
|
||||
{
|
||||
int i;
|
||||
sqlite3_stmt *statement;
|
||||
int res;
|
||||
|
||||
statement = prepareStatement(db,
|
||||
"INSERT OR IGNORE INTO event_kind (name, description, enum)"
|
||||
"VALUES (?, ?, ?)");
|
||||
|
||||
i = 0;
|
||||
EventKindENUM(EVENT_KIND_DO_INSERT, X);
|
||||
|
||||
finalizeStatement(db, statement);
|
||||
|
||||
statement = prepareStatement(db,
|
||||
"INSERT OR IGNORE INTO event_type (name, code, used, kind)"
|
||||
"VALUES (?, ?, ?, ?)");
|
||||
EVENT_LIST(EVENT_TYPE_DO_INSERT, X);
|
||||
|
||||
finalizeStatement(db, statement);
|
||||
|
||||
statement = prepareStatement(db,
|
||||
"INSERT OR IGNORE INTO event_param (type, param_index, sort, ident, doc)"
|
||||
"VALUES (?, ?, ?, ?, ?)");
|
||||
EVENT_LIST(EVENT_TYPE_INSERT_PARAMS, X);
|
||||
|
||||
finalizeStatement(db, statement);
|
||||
}
|
||||
|
||||
/* Populate the actual event tables. */
|
||||
|
||||
#define EVENT_TYPE_DECLARE_STATEMENT(X, name, code, used, kind) \
|
||||
sqlite3_stmt *stmt_##name;
|
||||
|
||||
#define EVENT_PARAM_PREPARE_IDENT(X, index, sort, ident, doc) "\"" #ident "\", "
|
||||
|
||||
#define EVENT_PARAM_PREPARE_PLACE(X, index, sort, ident, doc) "?, "
|
||||
|
||||
#define EVENT_TYPE_PREPARE_STATEMENT(X, name, code, used, kind) \
|
||||
stmt_##name = \
|
||||
prepareStatement(db, \
|
||||
"INSERT INTO EVENT_" #name " (" \
|
||||
EVENT_##name##_PARAMS(EVENT_PARAM_PREPARE_IDENT, X) \
|
||||
"log_serial, time) VALUES (" \
|
||||
EVENT_##name##_PARAMS(EVENT_PARAM_PREPARE_PLACE,X) \
|
||||
"?, ?)");
|
||||
|
||||
#define EVENT_TYPE_FINALIZE_STATEMENT(X, name, code, used, kind) \
|
||||
finalizeStatement(db, stmt_##name);
|
||||
|
||||
#define EVENT_PARAM_BIND_A bind_int
|
||||
#define EVENT_PARAM_BIND_P bind_int
|
||||
#define EVENT_PARAM_BIND_U bind_int
|
||||
#define EVENT_PARAM_BIND_W bind_int
|
||||
#define EVENT_PARAM_BIND_D bind_real
|
||||
#define EVENT_PARAM_BIND_S bind_text
|
||||
#define EVENT_PARAM_BIND_B bind_int
|
||||
|
||||
#define EVENT_PARAM_BIND(X, index, sort, ident, doc) \
|
||||
p = EVENT_PARAM_BIND_##sort (db, statement, eventCount, index+1, p); \
|
||||
last_index = index+1;
|
||||
|
||||
#define EVENT_TYPE_WRITE_SQL(X, name, code, used, kind) \
|
||||
case code: \
|
||||
statement = stmt_##name; \
|
||||
/* bind all the parameters of this particular event with macro magic. */ \
|
||||
EVENT_##name##_PARAMS(EVENT_PARAM_BIND, X) \
|
||||
break;
|
||||
|
||||
static char *bind_int(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int field, char *p)
|
||||
{
|
||||
char *q;
|
||||
long long val;
|
||||
int res;
|
||||
|
||||
while(*p == ' ')
|
||||
++p;
|
||||
|
||||
val = strtoll(p, &q, 16);
|
||||
if (q == p)
|
||||
error("event %llu field %d not an integer: %s",
|
||||
count, field, p);
|
||||
|
||||
res = sqlite3_bind_int64(stmt, field, val);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event %llu field %d bind failed", count, field);
|
||||
return q;
|
||||
}
|
||||
|
||||
static char *bind_real(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int field, char *p)
|
||||
{
|
||||
char *q;
|
||||
double val;
|
||||
int res;
|
||||
|
||||
while(*p == ' ')
|
||||
++p;
|
||||
|
||||
val = strtod(p, &q);
|
||||
if (q == p)
|
||||
error("event %llu field %d not a floating-point value: %s",
|
||||
count, field, p);
|
||||
|
||||
res = sqlite3_bind_double(stmt, field, val);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event %llu field %d bind failed", count, field);
|
||||
return q;
|
||||
}
|
||||
|
||||
static char *bind_text(sqlite3 *db, sqlite3_stmt *stmt, int64 count, int field, char *p)
|
||||
{
|
||||
char *q;
|
||||
int res;
|
||||
|
||||
while(*p == ' ')
|
||||
++p;
|
||||
|
||||
q = p;
|
||||
while((*q != '\n') && (*q != '\0')) {
|
||||
++ q;
|
||||
}
|
||||
if ((q == p) || (q[-1] != '"'))
|
||||
error("event %llu string field %d has no closing quote mark.",
|
||||
count, field);
|
||||
|
||||
res = sqlite3_bind_text(stmt, field, p, (int)(q-p-1), SQLITE_STATIC);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "event %llu field %d bind failed", count, field);
|
||||
return q;
|
||||
}
|
||||
|
||||
/* this is overkill, at present. */
|
||||
|
||||
#define MAX_LOG_LINE_LENGTH 1024
|
||||
|
||||
/* readLog -- read and parse log. Returns the number of events written.
|
||||
*/
|
||||
|
||||
static int64 readLog(FILE *input,
|
||||
sqlite3 *db)
|
||||
{
|
||||
int64 eventCount = 0;
|
||||
|
||||
/* declare statements for every event type */
|
||||
EVENT_LIST(EVENT_TYPE_DECLARE_STATEMENT, X);
|
||||
|
||||
/* prepare statements for every event type */
|
||||
EVENT_LIST(EVENT_TYPE_PREPARE_STATEMENT, X);
|
||||
|
||||
runStatement(db, "BEGIN", "Transaction start");
|
||||
|
||||
while (TRUE) { /* loop for each event */
|
||||
char line[MAX_LOG_LINE_LENGTH];
|
||||
char *p;
|
||||
char *q;
|
||||
int last_index=0;
|
||||
sqlite3_stmt *statement = NULL;
|
||||
int res;
|
||||
int64 clock_field;
|
||||
long code;
|
||||
|
||||
p = fgets(line, MAX_LOG_LINE_LENGTH, input);
|
||||
if (!p) {
|
||||
if (feof(input))
|
||||
break;
|
||||
else
|
||||
error("Couldn't read line after event %llu", eventCount);
|
||||
}
|
||||
|
||||
eventCount++;
|
||||
|
||||
clock_field = strtoll(p, &q, 16);
|
||||
|
||||
if (q == p)
|
||||
error("event %llu clock field not a hex integer: %s",
|
||||
eventCount, p);
|
||||
|
||||
if (*q != ' ')
|
||||
error("event %llu code field not preceded by ' ': %s",
|
||||
eventCount, q);
|
||||
while(*q == ' ')
|
||||
++q;
|
||||
|
||||
p = q;
|
||||
code = strtol(p, &q, 16);
|
||||
if (q == p)
|
||||
error("event %llu code field not an integer: %s",
|
||||
eventCount, p);
|
||||
p = q;
|
||||
|
||||
/* Write event to SQLite. */
|
||||
switch (code) {
|
||||
/* this macro sets statement and last_index */
|
||||
EVENT_LIST(EVENT_TYPE_WRITE_SQL, X);
|
||||
default:
|
||||
error("Event %llu has Unknown event code %ld", eventCount, code);
|
||||
}
|
||||
/* bind the fields we store for every event */ \
|
||||
res = sqlite3_bind_int64(statement, last_index+1, logSerial);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "Event %llu bind of log_serial failed.", eventCount);
|
||||
res = sqlite3_bind_int64(statement, last_index+2, clock_field);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "Event %llu bind of clock failed.", eventCount);
|
||||
res = sqlite3_step(statement);
|
||||
if (res != SQLITE_DONE)
|
||||
sqlite_error(res, db, "insert of event %llu failed.", eventCount);
|
||||
res = sqlite3_reset(statement);
|
||||
if (res != SQLITE_OK)
|
||||
sqlite_error(res, db, "Couldn't reset insert statement of event %llu", eventCount);
|
||||
|
||||
if (progress) {
|
||||
if ((eventCount % SMALL_TICK) == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
if (((eventCount / SMALL_TICK) % BIG_TICK) == 0) {
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
evlog(LOG_SOMETIMES, "%lu events.", (unsigned long)eventCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (progress) {
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
runStatement(db, "COMMIT", "Transaction finish");
|
||||
logFileCompleted(db, eventCount);
|
||||
|
||||
/* finalize all the statements */
|
||||
EVENT_LIST(EVENT_TYPE_FINALIZE_STATEMENT, X);
|
||||
|
||||
return eventCount;
|
||||
}
|
||||
|
||||
/* openLog -- open the log file doors, HAL */
|
||||
|
||||
static FILE *openLog(sqlite3 *db)
|
||||
{
|
||||
FILE *input;
|
||||
|
||||
registerLogFile(db, logFileName);
|
||||
if (!logFileName) {
|
||||
input = stdin;
|
||||
logFileName = "<stdin>";
|
||||
} else {
|
||||
input = fopen(logFileName, "r");
|
||||
if (input == NULL)
|
||||
error("unable to open %s", logFileName);
|
||||
}
|
||||
|
||||
evlog(LOG_OFTEN, "Reading %s.", logFileName ? logFileName : "standard input");
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
static int64 writeEventsToSQL(sqlite3 *db)
|
||||
{
|
||||
FILE *input;
|
||||
int64 count;
|
||||
input = openLog(db);
|
||||
count = readLog(input, db);
|
||||
(void)fclose(input);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
sqlite3 *db;
|
||||
int64 count;
|
||||
|
||||
parseArgs(argc, argv);
|
||||
|
||||
db = openDatabase();
|
||||
if (rebuild) {
|
||||
dropGlueTables(db);
|
||||
}
|
||||
makeTables(db);
|
||||
fillGlueTables(db);
|
||||
count = writeEventsToSQL(db);
|
||||
evlog(LOG_ALWAYS, "Imported %llu events from %s to %s, serial %llu.",
|
||||
count, logFileName, databaseName, logSerial);
|
||||
|
||||
if (runTests) {
|
||||
/* TODO: more unit tests in here */
|
||||
testTableExists(db);
|
||||
}
|
||||
|
||||
closeDatabase(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2012-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
661
mps/code/eventtxt.c
Normal file
661
mps/code/eventtxt.c
Normal file
|
|
@ -0,0 +1,661 @@
|
|||
/* eventtxt.c: event text log to human-friendly format.
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2012-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* This is a command-line tool that converts events from a text-format
|
||||
* MPS telemetry file into a more human-readable format.
|
||||
*
|
||||
* The default MPS library will write a binary-format telemetry file
|
||||
* which can be converted into a text-format file using the eventcnv
|
||||
* program (q.v.).
|
||||
*
|
||||
* For efficiency, eventcnv writes all event times, codes, and
|
||||
* parameters (apart from EventFS - strings - and EventFD -
|
||||
* floating-point) as hexadecimal strings, separated by single spaces.
|
||||
* For human-readable purposes, we'd prefer a format in which
|
||||
* parameters are named; event codes are converted to event type
|
||||
* names; integers are in decimal; booleans are 'True ' or 'False';
|
||||
* pointers, addresses, and words are in hex; and labelled addresses
|
||||
* are shown with their label strings. This program performs that
|
||||
* conversion.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* -l <logfile>: Import events from the named logfile. Defaults to
|
||||
* stdin.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include "check.h"
|
||||
#include "config.h"
|
||||
#include "eventcom.h"
|
||||
#include "eventdef.h"
|
||||
#include "mps.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscmvff.h"
|
||||
#include "table.h"
|
||||
#include "testlib.h" /* for ulongest_t and associated print formats */
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* exit, EXIT_FAILURE, EXIT_SUCCESS */
|
||||
#include <string.h> /* strcpy, strerror, strlen */
|
||||
|
||||
static const char *prog; /* program name */
|
||||
static const char *logFileName = NULL;
|
||||
|
||||
/* everror -- error signalling */
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 1, 2))
|
||||
static void everror(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
(void)fflush(stdout); /* sync */
|
||||
(void)fprintf(stderr, "%s: ", prog);
|
||||
va_start(args, format);
|
||||
(void)vfprintf(stderr, format, args);
|
||||
(void)fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
(void)fprintf(stderr, "Usage: %s [-l <logfile>]\n", prog);
|
||||
}
|
||||
|
||||
static void usageError(void)
|
||||
{
|
||||
usage();
|
||||
everror("Bad usage");
|
||||
}
|
||||
|
||||
/* parseArgs -- parse command line arguments */
|
||||
|
||||
static void parseArgs(int argc, char *argv[])
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
if (argc >= 1)
|
||||
prog = argv[0];
|
||||
else
|
||||
prog = "unknown";
|
||||
|
||||
while (i < argc) { /* consider argument i */
|
||||
if (argv[i][0] == '-') { /* it's an option argument */
|
||||
switch (argv[i][1]) {
|
||||
case 'l': /* log file name */
|
||||
++ i;
|
||||
if (i == argc)
|
||||
usageError();
|
||||
else
|
||||
logFileName = argv[i];
|
||||
break;
|
||||
case '?': case 'h': /* help */
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
usageError();
|
||||
}
|
||||
} /* if option */
|
||||
++ i;
|
||||
}
|
||||
}
|
||||
|
||||
/* table methods for a table of interned strings, and another of
|
||||
* labelled addresses. */
|
||||
|
||||
static void *tableAlloc(void *closure, size_t size)
|
||||
{
|
||||
mps_pool_t pool = closure;
|
||||
mps_addr_t p;
|
||||
mps_res_t res;
|
||||
res = mps_alloc(&p, pool, size);
|
||||
if (res != MPS_RES_OK)
|
||||
everror("allocation failed: %d", res);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void tableFree(void *closure, void *p, size_t size)
|
||||
{
|
||||
mps_pool_t pool = closure;
|
||||
mps_free(pool, p, size);
|
||||
}
|
||||
|
||||
/* Printing routines */
|
||||
|
||||
/* printStr -- print an EventString */
|
||||
|
||||
static void printStr(const char *str)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
putchar('"');
|
||||
for (i = 0; str[i] != '\0'; ++i) {
|
||||
char c = str[i];
|
||||
if (c == '"' || c == '\\')
|
||||
putchar('\\');
|
||||
putchar(c);
|
||||
}
|
||||
putchar('"');
|
||||
}
|
||||
|
||||
|
||||
/* Reading clocks, hex numbers, and doubles, and quoted-and-escaped
|
||||
* strings. */
|
||||
|
||||
static EventClock parseClock(char **pInOut)
|
||||
{
|
||||
EventClock val;
|
||||
int i, l;
|
||||
unsigned long low, high;
|
||||
char *p = *pInOut;
|
||||
|
||||
i = sscanf(p, "%08lX%08lX%n", &high, &low, &l);
|
||||
if (i != 2)
|
||||
everror("Couldn't read a clock from '%s'", p);
|
||||
EVENT_CLOCK_MAKE(val, low, high);
|
||||
|
||||
*pInOut = p + l;
|
||||
return val;
|
||||
}
|
||||
|
||||
static ulongest_t parseHex(char **pInOut)
|
||||
{
|
||||
ulongest_t val;
|
||||
int i, l;
|
||||
char *p = *pInOut;
|
||||
|
||||
i = sscanf(p, "%" SCNXLONGEST "%n", &val, &l);
|
||||
if (i != 1)
|
||||
everror("Couldn't read a hex number from '%s'", p);
|
||||
*pInOut = p + l;
|
||||
return val;
|
||||
}
|
||||
|
||||
static double parseDouble(char **pInOut)
|
||||
{
|
||||
double val;
|
||||
int i, l;
|
||||
char *p = *pInOut;
|
||||
|
||||
i = sscanf(p, "%lg%n", &val, &l);
|
||||
if (i != 1)
|
||||
everror("Couldn't read a float from '%s'", p);
|
||||
*pInOut = p + l;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* parseString checks string syntax (opening and closing quotation
|
||||
* marks) and takes a copy (stripping escaping backslashes) into a
|
||||
* static buffer (callers must "use it or lose it"; the next
|
||||
* invocation will over-write it). Probably not bullet-proof. */
|
||||
|
||||
#define MAX_STRING_LENGTH 1024
|
||||
|
||||
static char strBuf[MAX_STRING_LENGTH];
|
||||
|
||||
static char *parseString(char **pInOut)
|
||||
{
|
||||
char *p = *pInOut;
|
||||
char *q = strBuf;
|
||||
while(*p == ' ')
|
||||
++p;
|
||||
|
||||
if (*p != '"')
|
||||
everror("String has no opening quotation mark: '%s'", p);
|
||||
++p;
|
||||
|
||||
while(1) {
|
||||
if (q - strBuf >= MAX_STRING_LENGTH) {
|
||||
everror("String length exceeds %d", MAX_STRING_LENGTH);
|
||||
}
|
||||
if (*p == '\\') { /* escaped character */
|
||||
++p;
|
||||
if (*p == '\0')
|
||||
everror("Closing NUL byte escaped by backslash.");
|
||||
*q++ = *p++;
|
||||
} else if (*p == '"') { /* end of string */
|
||||
*q = '\0';
|
||||
++p;
|
||||
*pInOut = p;
|
||||
return strBuf;
|
||||
} else if (*p == '\0')
|
||||
everror("Unexpected closing NUL byte.");
|
||||
else
|
||||
*q++ = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Event logs have interned strings (i.e. they construct a partial
|
||||
* function from non-negative integer IDs to strings), and can label
|
||||
* addresses and pointers with intern string IDs (i.e. they construct
|
||||
* a partial function from address or pointer to string ID). We need
|
||||
* three tables to keep track of these. */
|
||||
|
||||
static Table internTable; /* dictionary of intern ids to strings */
|
||||
|
||||
static Table labelAddrTable; /* dictionary of addrs to intern ids */
|
||||
|
||||
static Table labelPointerTable; /* dictionary of pointers to intern ids */
|
||||
|
||||
static void createTables(mps_pool_t pool)
|
||||
{
|
||||
Res res;
|
||||
/* MPS intern IDs are serials from zero up, so we can use -1
|
||||
* and -2 as specials. */
|
||||
res = TableCreate(&internTable,
|
||||
(size_t)1<<4,
|
||||
tableAlloc, tableFree, pool,
|
||||
(TableKey)-1, (TableKey)-2);
|
||||
if (res != ResOK)
|
||||
everror("Couldn't make intern table.");
|
||||
|
||||
/* We assume that 0 and 1 are invalid as Addrs. */
|
||||
res = TableCreate(&labelAddrTable, (size_t)1<<7,
|
||||
tableAlloc, tableFree, pool,
|
||||
0, 1);
|
||||
if (res != ResOK)
|
||||
everror("Couldn't make address label table.");
|
||||
|
||||
/* We assume that 0 and 1 are invalid as Pointers. */
|
||||
res = TableCreate(&labelPointerTable, (size_t)1<<7,
|
||||
tableAlloc, tableFree, pool,
|
||||
0, 1);
|
||||
if (res != ResOK)
|
||||
everror("Couldn't make pointer label table.");
|
||||
}
|
||||
|
||||
/* recordIntern -- record an interned string in the table. a copy of
|
||||
* the string from the parsed buffer into a newly-allocated block. */
|
||||
|
||||
static void recordIntern(mps_pool_t pool, char *p)
|
||||
{
|
||||
ulongest_t stringId;
|
||||
char *string;
|
||||
mps_addr_t copy;
|
||||
size_t len;
|
||||
Res res;
|
||||
|
||||
stringId = parseHex(&p);
|
||||
string = parseString(&p);
|
||||
len = strlen(string);
|
||||
res = mps_alloc(©, pool, len + 1);
|
||||
if (res != MPS_RES_OK)
|
||||
everror("Couldn't allocate space for a string.");
|
||||
(void)strcpy(copy, string);
|
||||
res = TableDefine(internTable, (TableKey)stringId, (void *)copy);
|
||||
if (res != ResOK)
|
||||
everror("Couldn't create an intern mapping.");
|
||||
}
|
||||
|
||||
/* Over time there may be multiple labels associated with an address,
|
||||
* so we keep a list, recording for each label the clock when the
|
||||
* association was made. This means that printAddr can select the
|
||||
* label that was in force at the time of the event.
|
||||
*/
|
||||
|
||||
typedef struct LabelStruct *Label;
|
||||
typedef struct LabelStruct {
|
||||
EventClock clock; /* clock of this label */
|
||||
ulongest_t id; /* string id of this label */
|
||||
} LabelStruct;
|
||||
|
||||
typedef struct LabelListStruct *LabelList;
|
||||
typedef struct LabelListStruct {
|
||||
size_t n; /* number of labels in array */
|
||||
Label labels; /* labels, sorted in order by clock */
|
||||
} LabelListStruct;
|
||||
|
||||
/* labelFind returns the index of the first entry in list with a clock
|
||||
* value that's greater than 'clock', or list->n if there is no such
|
||||
* label. The list is assumed to be sorted.
|
||||
*/
|
||||
|
||||
static size_t labelFind(LabelList list, EventClock clock)
|
||||
{
|
||||
size_t low = 0, high = list->n;
|
||||
while (low < high) {
|
||||
size_t mid = (low + high) / 2;
|
||||
assert(NONNEGATIVE(mid) && mid < list->n);
|
||||
if (list->labels[mid].clock > clock) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
assert(NONNEGATIVE(low) && low <= list->n);
|
||||
assert(low == list->n || list->labels[low].clock > clock);
|
||||
return low;
|
||||
}
|
||||
|
||||
/* recordLabel records a label: an association (made at the time given
|
||||
* by 'clock') between a client address or an internal pointer and a
|
||||
* string ID. These are encoded as two hexadecimal numbers in the
|
||||
* string pointed to by 'p'.
|
||||
*
|
||||
* Note that the event log may have been generated on a platform with
|
||||
* addresses larger than Word on the current platform. If that happens
|
||||
* then we are scuppered because our Table code uses Word as the key
|
||||
* type: there's nothing we can do except detect this bad case (see
|
||||
* also the EventInit handling and warning code).
|
||||
*
|
||||
* We can and do handle the case where string IDs (which are Words on
|
||||
* the MPS platform) are larger than void* on the current platform.
|
||||
* This is probably in fact the same case, because Word should be the
|
||||
* same size as void*. In practice, trying to analyse a log from a
|
||||
* wide platform on a narrow one (e.g. - the only case which is likely
|
||||
* to occur this decade - from a 64-bit platform on a 32-bit one) is
|
||||
* probably a bad idea and maybe doomed to failure.
|
||||
*/
|
||||
|
||||
static void recordLabel(mps_pool_t pool, Table table, EventClock clock, char *p)
|
||||
{
|
||||
ulongest_t address;
|
||||
LabelList list;
|
||||
Label newlabels;
|
||||
mps_addr_t tmp;
|
||||
size_t pos;
|
||||
Res res;
|
||||
|
||||
address = parseHex(&p);
|
||||
if (address > (Word)-1) {
|
||||
(void)printf("label address too large!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TableLookup(&tmp, table, (TableKey)address)) {
|
||||
list = tmp;
|
||||
} else {
|
||||
/* First label for this address */
|
||||
res = mps_alloc(&tmp, pool, sizeof(LabelListStruct));
|
||||
if (res != MPS_RES_OK)
|
||||
everror("Can't allocate space for a label list");
|
||||
list = tmp;
|
||||
list->n = 0;
|
||||
res = TableDefine(table, (TableKey)address, list);
|
||||
if (res != ResOK)
|
||||
everror("Couldn't create a label mapping.");
|
||||
}
|
||||
|
||||
res = mps_alloc(&tmp, pool, sizeof(LabelStruct) * (list->n + 1));
|
||||
if (res != ResOK)
|
||||
everror("Couldn't allocate space for list of labels.");
|
||||
newlabels = tmp;
|
||||
|
||||
pos = labelFind(list, clock);
|
||||
memcpy(newlabels, list->labels, sizeof(LabelStruct) * pos);
|
||||
newlabels[pos].clock = clock;
|
||||
newlabels[pos].id = parseHex(&p);
|
||||
memcpy(newlabels + pos + 1, list->labels + pos,
|
||||
sizeof(LabelStruct) * (list->n - pos));
|
||||
if (list->n > 0)
|
||||
mps_free(pool, list->labels, sizeof(LabelStruct) * list->n);
|
||||
list->labels = newlabels;
|
||||
++ list->n;
|
||||
}
|
||||
|
||||
/* output code */
|
||||
|
||||
/* hexWordWidth is the number of characters used to output a Word
|
||||
* value in hexadecimal. Note that what we really care about is the
|
||||
* width of a Word on the source platform, not here. So when we see
|
||||
* an EventInit event, we update this variable to the necessary
|
||||
* width. */
|
||||
|
||||
static int hexWordWidth = (MPS_WORD_WIDTH+3)/4;
|
||||
|
||||
/* printLabelled -- output a ulongest_t in hex, with the interned
|
||||
* string if the value is in the table */
|
||||
|
||||
static void printLabelled(EventClock clock, ulongest_t value,
|
||||
const char *ident, Table table)
|
||||
{
|
||||
void *tmp;
|
||||
|
||||
printf("%s:%0*" PRIXLONGEST, ident, hexWordWidth, value);
|
||||
if (table != NULL && TableLookup(&tmp, table, (TableKey)value)) {
|
||||
LabelList list = tmp;
|
||||
size_t pos = labelFind(list, clock);
|
||||
if (pos > 0) {
|
||||
ulongest_t id = list->labels[pos - 1].id;
|
||||
putchar('[');
|
||||
if (TableLookup(&tmp, internTable, (TableKey)id))
|
||||
printStr((char *)tmp);
|
||||
else
|
||||
printf("unknown label %" PRIXLONGEST, id);
|
||||
putchar(']');
|
||||
}
|
||||
}
|
||||
putchar(' ');
|
||||
}
|
||||
|
||||
/* parameter processing. For each parameter we parse it and then
|
||||
* print it, preceded by its name and a colon and followed by a
|
||||
* space. */
|
||||
|
||||
#define processParamA(ident) \
|
||||
val_hex = parseHex(&p); \
|
||||
printLabelled(clock, val_hex, #ident, labelAddrTable);
|
||||
|
||||
#define processParamP(ident) \
|
||||
val_hex = parseHex(&p); \
|
||||
printLabelled(clock, val_hex, #ident, labelPointerTable);
|
||||
|
||||
#define processParamW(ident) \
|
||||
val_hex = parseHex(&p); \
|
||||
printLabelled(clock, val_hex, #ident, NULL);
|
||||
|
||||
#define processParamU(ident) \
|
||||
val_hex = parseHex(&p); \
|
||||
printf(#ident ":%" PRIuLONGEST " ", val_hex);
|
||||
|
||||
#define processParamD(ident) \
|
||||
val_float = parseDouble(&p); \
|
||||
printf(#ident ":%#8.3g ", val_float);
|
||||
|
||||
#define processParamS(ident) \
|
||||
val_string = parseString(&p); \
|
||||
printf(#ident ":"); \
|
||||
printStr(val_string); \
|
||||
putchar(' ');
|
||||
|
||||
#define processParamB(ident) \
|
||||
val_hex = parseHex(&p); \
|
||||
printf(#ident ":%s ", val_hex ? "True" : "False");
|
||||
|
||||
#define EVENT_PROCESS_PARAM(X, index, sort, ident, doc) \
|
||||
processParam##sort(ident);
|
||||
|
||||
#define EVENT_PROCESS(X, name, code, used, kind) \
|
||||
case code: \
|
||||
EVENT_##name##_PARAMS(EVENT_PROCESS_PARAM, X) \
|
||||
break;
|
||||
|
||||
/* a table of the event names */
|
||||
|
||||
static const char *eventName[EventCodeMAX+EventCodeMAX];
|
||||
|
||||
#define EVENT_SET_NAME(X, name, code, used, kind) \
|
||||
eventName[code] = #name;
|
||||
|
||||
/* this is overkill, at present. */
|
||||
|
||||
#define MAX_LOG_LINE_LENGTH 1024
|
||||
|
||||
/* readLog -- read and parse log. Returns the number of events written. */
|
||||
|
||||
static void readLog(mps_pool_t pool, FILE *input)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i <= EventCodeMAX; ++i)
|
||||
eventName[i] = NULL;
|
||||
|
||||
EVENT_LIST(EVENT_SET_NAME, X);
|
||||
|
||||
while (TRUE) { /* loop for each event */
|
||||
char line[MAX_LOG_LINE_LENGTH];
|
||||
char *p, *q;
|
||||
EventClock clock;
|
||||
int code;
|
||||
ulongest_t val_hex;
|
||||
double val_float;
|
||||
const char *val_string;
|
||||
|
||||
p = fgets(line, MAX_LOG_LINE_LENGTH, input);
|
||||
if (!p) {
|
||||
if (feof(input))
|
||||
break;
|
||||
else
|
||||
everror("Couldn't read line from input.");
|
||||
}
|
||||
|
||||
clock = parseClock(&p);
|
||||
EVENT_CLOCK_PRINT(stdout, clock);
|
||||
|
||||
code = (int)parseHex(&p);
|
||||
printf(" %04X ", code);
|
||||
if (eventName[code])
|
||||
printf("%-19s ", eventName[code]);
|
||||
else
|
||||
printf("%-19s ", "[Unknown]");
|
||||
|
||||
q = p;
|
||||
|
||||
/* for a few particular codes, we do local processing. */
|
||||
if (code == EventInternCode) {
|
||||
recordIntern(pool, q);
|
||||
} else if (code == EventLabelCode) {
|
||||
recordLabel(pool, labelAddrTable, clock, q);
|
||||
} else if (code == EventLabelPointerCode) {
|
||||
recordLabel(pool, labelPointerTable, clock, q);
|
||||
} else if (code == EventEventInitCode) {
|
||||
ulongest_t major, median, minor, maxCode, maxNameLen, wordWidth, clocksPerSec;
|
||||
major = parseHex(&q); /* EVENT_VERSION_MAJOR */
|
||||
median = parseHex(&q); /* EVENT_VERSION_MEDIAN */
|
||||
minor = parseHex(&q); /* EVENT_VERSION_MINOR */
|
||||
maxCode = parseHex(&q); /* EventCodeMAX */
|
||||
maxNameLen = parseHex(&q); /* EventNameMAX */
|
||||
wordWidth = parseHex(&q); /* MPS_WORD_WIDTH */
|
||||
clocksPerSec = parseHex(&q); /* mps_clocks_per_sec() */
|
||||
UNUSED(clocksPerSec);
|
||||
UNUSED(maxNameLen);
|
||||
|
||||
if ((major != EVENT_VERSION_MAJOR) ||
|
||||
(median != EVENT_VERSION_MEDIAN) ||
|
||||
(minor != EVENT_VERSION_MINOR)) {
|
||||
(void)fprintf(stderr, "Event log version does not match: "
|
||||
"%d.%d.%d vs %d.%d.%d\n",
|
||||
(int)major, (int)median, (int)minor,
|
||||
EVENT_VERSION_MAJOR,
|
||||
EVENT_VERSION_MEDIAN,
|
||||
EVENT_VERSION_MINOR);
|
||||
}
|
||||
|
||||
if (maxCode > EventCodeMAX) {
|
||||
(void)fprintf(stderr, "Event log may contain unknown events "
|
||||
"with codes from %d to %d\n",
|
||||
EventCodeMAX+1, (int)maxCode);
|
||||
}
|
||||
|
||||
if (wordWidth > MPS_WORD_WIDTH) {
|
||||
int newHexWordWidth = (int)((wordWidth + 3) / 4);
|
||||
if (newHexWordWidth > hexWordWidth) {
|
||||
(void)fprintf(stderr,
|
||||
"Event log word width is greater than on current "
|
||||
"platform; previous values may be printed too "
|
||||
"narrowly.\n");
|
||||
}
|
||||
hexWordWidth = newHexWordWidth;
|
||||
}
|
||||
|
||||
if (wordWidth > sizeof(ulongest_t) * CHAR_BIT) {
|
||||
everror("Event log word width %d is too wide for the current platform.",
|
||||
(int)wordWidth);
|
||||
}
|
||||
}
|
||||
|
||||
switch(code) {
|
||||
EVENT_LIST(EVENT_PROCESS, X);
|
||||
default:
|
||||
printf("Unknown event.");
|
||||
}
|
||||
putchar('\n');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_pool_t pool;
|
||||
mps_res_t res;
|
||||
FILE *input;
|
||||
|
||||
parseArgs(argc, argv);
|
||||
if (!logFileName) {
|
||||
input = stdin;
|
||||
logFileName = "<stdin>";
|
||||
} else {
|
||||
input = fopen(logFileName, "r");
|
||||
if (input == NULL)
|
||||
everror("unable to open %s", logFileName);
|
||||
}
|
||||
|
||||
/* Ensure no telemetry output. */
|
||||
res = setenv("MPS_TELEMETRY_CONTROL", "0", 1);
|
||||
if (res != 0)
|
||||
everror("failed to set MPS_TELEMETRY_CONTROL: %s", strerror(errno));
|
||||
|
||||
res = mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none);
|
||||
if (res != MPS_RES_OK)
|
||||
everror("failed to create arena: %d", res);
|
||||
|
||||
res = mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none);
|
||||
if (res != MPS_RES_OK)
|
||||
everror("failed to create pool: %d", res);
|
||||
|
||||
createTables(pool);
|
||||
readLog(pool, input);
|
||||
|
||||
mps_pool_destroy(pool);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
(void)fclose(input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2012-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
306
mps/code/extcon.c
Normal file
306
mps/code/extcon.c
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
/* extcon.c: ARENA EXTENDED AND CONTRACTED CALLBACK TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2022-2023 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview: This test case allocates a bunch of large objects, of a size
|
||||
* similar to the size of the arena, to force the arena to extend. It then
|
||||
* discards the base pointers to those objects, and forces a collection.
|
||||
*
|
||||
* .limitations: This test checks that the EXTENDED and CONTRACTED
|
||||
* callbacks were called at least once, and that they are called the
|
||||
* same number of times. It does not check that the extensions and
|
||||
* contractions themselves were performed correctly, nor does it check
|
||||
* that an appropriate number of extensions and contractions took
|
||||
* place, nor does it check that they took place at sensible times.
|
||||
*
|
||||
* .dylan: This test uses Dylan format objects in common with most
|
||||
* other tests for convenience and brevity.
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscamc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Number of test objects to allocate */
|
||||
#define N_TESTOBJ 100
|
||||
/* The number of slots determines the size of each object */
|
||||
#define N_SLOT_TESTOBJ 10000
|
||||
/* The initial arena size is requested to be bigger the test object by
|
||||
this many bytes */
|
||||
#define SIZEDIFF 10
|
||||
|
||||
/* Set alignment to mps_word_ts */
|
||||
#define ALIGNMENT sizeof(mps_word_t)
|
||||
|
||||
/* Align size upwards to the next multiple of the word size. */
|
||||
#define ALIGN_WORD(size) \
|
||||
(((size) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
|
||||
|
||||
/* Global objects*/
|
||||
static mps_arena_t arena; /* the arena */
|
||||
static mps_pool_t obj_pool; /* pool for test objects */
|
||||
static mps_ap_t obj_ap; /* allocation point used to allocate objects */
|
||||
|
||||
/* Count of number of arena contractions and extensions */
|
||||
static int n_contract = 0;
|
||||
static int n_extend = 0;
|
||||
|
||||
/* Callback functions for arena extension and contraction */
|
||||
static void arena_extended_cb(mps_arena_t arena_in, mps_addr_t addr, size_t size)
|
||||
{
|
||||
testlib_unused(arena_in);
|
||||
testlib_unused(addr);
|
||||
testlib_unused(size);
|
||||
printf("Arena extended by %"PRIuLONGEST" bytes\n", (ulongest_t)size);
|
||||
n_extend++;
|
||||
}
|
||||
|
||||
static void arena_contracted_cb(mps_arena_t arena_in, mps_addr_t addr, size_t size)
|
||||
{
|
||||
testlib_unused(arena_in);
|
||||
testlib_unused(addr);
|
||||
testlib_unused(size);
|
||||
printf("Arena contracted by %"PRIuLONGEST" bytes\n", (ulongest_t)size);
|
||||
n_contract++;
|
||||
}
|
||||
|
||||
/* Messages for testbench debugging */
|
||||
static void print_messages(void)
|
||||
{
|
||||
mps_message_type_t type;
|
||||
|
||||
while (mps_message_queue_type(&type, arena)) {
|
||||
mps_message_t message;
|
||||
|
||||
cdie(mps_message_get(&message, arena, type),
|
||||
"get");
|
||||
|
||||
switch(type) {
|
||||
case mps_message_type_gc_start():
|
||||
printf("GC start at %"PRIuLONGEST": %s\n",
|
||||
(ulongest_t)mps_message_clock(arena, message),
|
||||
mps_message_gc_start_why(arena, message));
|
||||
break;
|
||||
|
||||
case mps_message_type_gc():
|
||||
printf("GC end at %"PRIuLONGEST" "
|
||||
"condemned %"PRIuLONGEST" "
|
||||
"not condemned %"PRIuLONGEST" "
|
||||
"live %"PRIuLONGEST"\n",
|
||||
(ulongest_t)mps_message_clock(arena, message),
|
||||
(ulongest_t)mps_message_gc_condemned_size(arena, message),
|
||||
(ulongest_t)mps_message_gc_not_condemned_size(arena, message),
|
||||
(ulongest_t)mps_message_gc_live_size(arena, message));
|
||||
break;
|
||||
|
||||
default:
|
||||
cdie(0, "message type");
|
||||
break;
|
||||
}
|
||||
|
||||
mps_message_discard(arena, message);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disabling inlining is necessary (but perhaps not sufficient) if using stack roots.
|
||||
See comment below with link to GitHub issue*/
|
||||
ATTRIBUTE_NOINLINE
|
||||
static void test_main(void *cold_stack_end)
|
||||
{
|
||||
mps_fmt_t obj_fmt;
|
||||
mps_thr_t thread;
|
||||
mps_root_t stack_root, testobj_root;
|
||||
size_t arena_size, obj_size;
|
||||
int i;
|
||||
/* In the original version of extcon this was a stack root, but we
|
||||
observed unreliable failures to do with registering the cold end
|
||||
of the stack. See GitHub issue #210
|
||||
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
|
||||
declare this as a separate root. */
|
||||
static mps_word_t testobj[N_TESTOBJ];
|
||||
|
||||
/* The testobj array must be below (on all current Posix platforms)
|
||||
the cold end of the stack in order for the MPS to scan it. We
|
||||
have observed a Heisenbug where GCC will inline test_main into
|
||||
main and lose this condition if the expression below is removed.
|
||||
This is a problem we are analysing in GitHub issue #210
|
||||
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
|
||||
disable this Insist to allow the test to run with a static
|
||||
testobj array. */
|
||||
#if 0
|
||||
Insist((void *)&testobj[N_TESTOBJ] <= cold_stack_end);
|
||||
if ((void *)&testobj[N_TESTOBJ] > cold_stack_end)
|
||||
printf("Cold stack marker invalid!\n");
|
||||
else
|
||||
printf("Cold stack marker probably valid.\n");
|
||||
#endif
|
||||
|
||||
/* Make initial arena size slightly bigger than the test object size to force an extension as early as possible */
|
||||
/* See definition of make_dylan_vector() in fmtdytst.c for calculation of vector size */
|
||||
obj_size = ALIGN_WORD((N_SLOT_TESTOBJ + 2) * sizeof(mps_word_t));
|
||||
arena_size = ALIGN_WORD(obj_size + SIZEDIFF);
|
||||
|
||||
/* Create arena and register callbacks */
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)&arena_extended_cb);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)&arena_contracted_cb);
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "mps_arena_create_k");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
printf("Initial reservation %"PRIuLONGEST".\n", (ulongest_t)mps_arena_reserved(arena));
|
||||
|
||||
die(dylan_fmt(&obj_fmt, arena), "dylan_fmt()");
|
||||
|
||||
/* Create new pool */
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
|
||||
die(mps_pool_create_k(&obj_pool, arena, mps_class_amcz(), args),
|
||||
"mps_pool_create_k");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
/* Register thread */
|
||||
die(mps_thread_reg(&thread, arena), "Thread reg");
|
||||
|
||||
/* Register stack roots */
|
||||
/* Since this testbench is currently not using a stack root, #IF 0 this out */
|
||||
testlib_unused(cold_stack_end);
|
||||
testlib_unused(stack_root);
|
||||
#if 0
|
||||
die(mps_root_create_thread(&stack_root, arena, thread, cold_stack_end), "Create Stack root");
|
||||
#endif
|
||||
|
||||
/* Register ambiguous array of object roots. */
|
||||
die(mps_root_create_area(&testobj_root, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&testobj[0], &testobj[N_TESTOBJ],
|
||||
mps_scan_area, NULL),
|
||||
"root_create_area(testobj)");
|
||||
|
||||
/* Create allocation point */
|
||||
die(mps_ap_create_k(&obj_ap, obj_pool, mps_args_none), "Create Allocation point");
|
||||
|
||||
mps_message_type_enable(arena, mps_message_type_gc_start());
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
|
||||
/* Allocate objects and force arena extension */
|
||||
for (i = 0; i < N_TESTOBJ; i++) {
|
||||
|
||||
die(make_dylan_vector(&testobj[i], obj_ap, N_SLOT_TESTOBJ), "make_dylan_vector");
|
||||
|
||||
printf("Object %d committed. "
|
||||
"Arena reserved: %"PRIuLONGEST".\n",
|
||||
i,
|
||||
(ulongest_t)mps_arena_reserved(arena));
|
||||
|
||||
print_messages();
|
||||
}
|
||||
|
||||
/* overwrite all the references to the objects*/
|
||||
for (i = 0; i < N_TESTOBJ; i++) {
|
||||
|
||||
/* bonus test of mps_addr_object */
|
||||
#if 0 /* Comment this out until mps_addr_object becomes available. */
|
||||
mps_addr_t out;
|
||||
Insist(N_TESTOBJ <= N_INT_TESTOBJ);
|
||||
|
||||
/* use "i" to as a convenient way to generate different interior pointers
|
||||
To guarantee the i index will give us an interior pointer the number of test
|
||||
objects must be <= the number of integers in each object */
|
||||
Insist(N_TESTOBJ <= N_INT_TESTOBJ);
|
||||
die(mps_addr_object(&out, arena, &(testobj[i])->int_array[i]), "Address object");
|
||||
|
||||
Insist(out == testobj[i]);
|
||||
|
||||
/* end piggy back testbench */
|
||||
#endif
|
||||
|
||||
/* now overwrite the ref */
|
||||
testobj[i] = (mps_word_t)NULL;
|
||||
|
||||
print_messages();
|
||||
}
|
||||
|
||||
/* Collect */
|
||||
mps_arena_collect(arena);
|
||||
|
||||
print_messages();
|
||||
|
||||
/* Clean up */
|
||||
mps_root_destroy(testobj_root);
|
||||
/* mps_root_destroy(stack_root);*/ /*commented out while not using stack root */
|
||||
mps_thread_dereg(thread);
|
||||
mps_ap_destroy(obj_ap);
|
||||
mps_pool_destroy(obj_pool);
|
||||
mps_fmt_destroy(obj_fmt);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
/* Destroying the arena should cause contraction callbacks on all
|
||||
remaining chunks, even if they had contents. */
|
||||
Insist(n_extend == n_contract);
|
||||
|
||||
printf("Arena extended %d times\n", n_extend);
|
||||
printf("Arena contracted %d times\n", n_contract);
|
||||
|
||||
/* comment out some diagnostics for investigating issue #210 mentioned above */
|
||||
#if 0
|
||||
printf("&testobj[N_TESTOBJ] = %p\n", (void *)&testobj[N_TESTOBJ]);
|
||||
printf("cold_stack_end = %p\n", cold_stack_end);
|
||||
#endif
|
||||
if (n_extend == 0)
|
||||
printf("No callbacks received upon arena extended!\n");
|
||||
if (n_contract == 0)
|
||||
printf("No callbacks received upon arena contracted!\n");
|
||||
|
||||
if (n_contract == 0 || n_extend == 0)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
void *stack_marker = &stack_marker;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
test_main(stack_marker);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2022-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
369
mps/code/failover.c
Normal file
369
mps/code/failover.c
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
/* failover.c: FAILOVER IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2014-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .design: <design/failover>
|
||||
*
|
||||
* .critical: In manual-allocation-bound programs using MVFF, many of
|
||||
* these functions are on the critical paths via mps_alloc (and then
|
||||
* PoolAlloc, MVFFAlloc, failoverFind*) and mps_free (and then
|
||||
* MVFFFree, failoverInsert).
|
||||
*/
|
||||
|
||||
#include "failover.h"
|
||||
#include "mpm.h"
|
||||
#include "range.h"
|
||||
|
||||
SRCID(failover, "$Id$");
|
||||
|
||||
|
||||
ARG_DEFINE_KEY(failover_primary, Pointer);
|
||||
ARG_DEFINE_KEY(failover_secondary, Pointer);
|
||||
|
||||
|
||||
Bool FailoverCheck(Failover fo)
|
||||
{
|
||||
CHECKS(Failover, fo);
|
||||
CHECKD(Land, &fo->landStruct);
|
||||
CHECKD(Land, fo->primary);
|
||||
CHECKD(Land, fo->secondary);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static Res failoverInit(Land land, Arena arena, Align alignment, ArgList args)
|
||||
{
|
||||
Failover fo;
|
||||
ArgStruct arg;
|
||||
Res res;
|
||||
|
||||
AVER(land != NULL);
|
||||
res = NextMethod(Land, Failover, init)(land, arena, alignment, args);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
fo = CouldBeA(Failover, land);
|
||||
|
||||
ArgRequire(&arg, args, FailoverPrimary);
|
||||
fo->primary = arg.val.p;
|
||||
ArgRequire(&arg, args, FailoverSecondary);
|
||||
fo->secondary = arg.val.p;
|
||||
|
||||
SetClassOfPoly(land, CLASS(Failover));
|
||||
fo->sig = FailoverSig;
|
||||
AVERC(Failover, fo);
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
static void failoverFinish(Inst inst)
|
||||
{
|
||||
Land land = MustBeA(Land, inst);
|
||||
Failover fo = MustBeA(Failover, land);
|
||||
fo->sig = SigInvalid;
|
||||
NextMethod(Inst, Failover, finish)(inst);
|
||||
}
|
||||
|
||||
|
||||
static Size failoverSize(Land land)
|
||||
{
|
||||
Failover fo = MustBeA_CRITICAL(Failover, land);
|
||||
return LandSize(fo->primary) + LandSize(fo->secondary);
|
||||
}
|
||||
|
||||
|
||||
static Res failoverInsert(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Failover fo = MustBeA_CRITICAL(Failover, land);
|
||||
Res res;
|
||||
|
||||
AVER_CRITICAL(rangeReturn != NULL);
|
||||
AVERT_CRITICAL(Range, range);
|
||||
|
||||
/* Provide more opportunities for coalescence. See
|
||||
* <design/failover#.impl.assume.flush>.
|
||||
*/
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
res = LandInsert(rangeReturn, fo->primary, range);
|
||||
if (res != ResOK && res != ResFAIL)
|
||||
res = LandInsert(rangeReturn, fo->secondary, range);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static Res failoverInsertSteal(Range rangeReturn, Land land, Range rangeIO)
|
||||
{
|
||||
Failover fo = MustBeA(Failover, land);
|
||||
Res res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(rangeReturn != rangeIO);
|
||||
AVERT(Range, rangeIO);
|
||||
|
||||
/* Provide more opportunities for coalescence. See
|
||||
* <design/failover#.impl.assume.flush>.
|
||||
*/
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
res = LandInsertSteal(rangeReturn, fo->primary, rangeIO);
|
||||
AVER(res == ResOK || res == ResFAIL);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static Res failoverDelete(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Failover fo = MustBeA(Failover, land);
|
||||
Res res;
|
||||
RangeStruct oldRange, dummyRange, left, right;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERT(Range, range);
|
||||
|
||||
/* Prefer efficient search in the primary. See
|
||||
* <design/failover#.impl.assume.flush>.
|
||||
*/
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
res = LandDelete(&oldRange, fo->primary, range);
|
||||
|
||||
if (res == ResFAIL) {
|
||||
/* Range not found in primary: try secondary. */
|
||||
return LandDelete(rangeReturn, fo->secondary, range);
|
||||
} else if (res != ResOK) {
|
||||
/* Range was found in primary, but couldn't be deleted. The only
|
||||
* case we expect to encounter here is the case where the primary
|
||||
* is out of memory. (In particular, we don't handle the case of a
|
||||
* CBS returning ResLIMIT because its block pool has been
|
||||
* configured not to automatically extend itself.)
|
||||
*/
|
||||
AVER(ResIsAllocFailure(res));
|
||||
|
||||
/* Delete the whole of oldRange, and re-insert the fragments
|
||||
* (which might end up in the secondary). See
|
||||
* <design/failover#.impl.assume.delete>.
|
||||
*/
|
||||
res = LandDelete(&dummyRange, fo->primary, &oldRange);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
AVER(RangesEqual(&oldRange, &dummyRange));
|
||||
RangeInit(&left, RangeBase(&oldRange), RangeBase(range));
|
||||
if (!RangeIsEmpty(&left)) {
|
||||
/* Don't call LandInsert(..., land, ...) here: that would be
|
||||
* re-entrant and fail the landEnter check. */
|
||||
res = LandInsert(&dummyRange, fo->primary, &left);
|
||||
if (res != ResOK) {
|
||||
/* The range was successful deleted from the primary above. */
|
||||
AVER(res != ResFAIL);
|
||||
res = LandInsert(&dummyRange, fo->secondary, &left);
|
||||
AVER(res == ResOK);
|
||||
}
|
||||
}
|
||||
RangeInit(&right, RangeLimit(range), RangeLimit(&oldRange));
|
||||
if (!RangeIsEmpty(&right)) {
|
||||
res = LandInsert(&dummyRange, fo->primary, &right);
|
||||
if (res != ResOK) {
|
||||
/* The range was successful deleted from the primary above. */
|
||||
AVER(res != ResFAIL);
|
||||
res = LandInsert(&dummyRange, fo->secondary, &right);
|
||||
AVER(res == ResOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res == ResOK) {
|
||||
AVER_CRITICAL(RangesNest(&oldRange, range));
|
||||
RangeCopy(rangeReturn, &oldRange);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static Res failoverDeleteSteal(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Failover fo = MustBeA(Failover, land);
|
||||
Res res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERT(Range, range);
|
||||
|
||||
/* Prefer efficient search in the primary. See
|
||||
* <design/failover#.impl.assume.flush>.
|
||||
*/
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
res = LandDeleteSteal(rangeReturn, fo->primary, range);
|
||||
if (res == ResFAIL)
|
||||
/* Not found in primary: try secondary. */
|
||||
res = LandDeleteSteal(rangeReturn, fo->secondary, range);
|
||||
AVER(res == ResOK || res == ResFAIL);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static Bool failoverIterate(Land land, LandVisitor visitor, void *closure)
|
||||
{
|
||||
Failover fo = MustBeA(Failover, land);
|
||||
|
||||
AVER(visitor != NULL);
|
||||
|
||||
return LandIterate(fo->primary, visitor, closure)
|
||||
&& LandIterate(fo->secondary, visitor, closure);
|
||||
}
|
||||
|
||||
|
||||
static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Failover fo = MustBeA_CRITICAL(Failover, land);
|
||||
|
||||
AVER_CRITICAL(rangeReturn != NULL);
|
||||
AVER_CRITICAL(oldRangeReturn != NULL);
|
||||
AVERT_CRITICAL(FindDelete, findDelete);
|
||||
|
||||
/* <design/failover#.impl.assume.flush>. */
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
return LandFindFirst(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
|
||||
|| LandFindFirst(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
|
||||
}
|
||||
|
||||
|
||||
static Bool failoverFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Failover fo = MustBeA_CRITICAL(Failover, land);
|
||||
|
||||
AVER_CRITICAL(rangeReturn != NULL);
|
||||
AVER_CRITICAL(oldRangeReturn != NULL);
|
||||
AVERT_CRITICAL(FindDelete, findDelete);
|
||||
|
||||
/* <design/failover#.impl.assume.flush>. */
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
return LandFindLast(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
|
||||
|| LandFindLast(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
|
||||
}
|
||||
|
||||
|
||||
static Bool failoverFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Failover fo = MustBeA_CRITICAL(Failover, land);
|
||||
|
||||
AVER_CRITICAL(rangeReturn != NULL);
|
||||
AVER_CRITICAL(oldRangeReturn != NULL);
|
||||
AVERT_CRITICAL(FindDelete, findDelete);
|
||||
|
||||
/* <design/failover#.impl.assume.flush>. */
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
return LandFindLargest(rangeReturn, oldRangeReturn, fo->primary, size, findDelete)
|
||||
|| LandFindLargest(rangeReturn, oldRangeReturn, fo->secondary, size, findDelete);
|
||||
}
|
||||
|
||||
|
||||
static Bool failoverFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)
|
||||
{
|
||||
Failover fo = MustBeA_CRITICAL(Failover, land);
|
||||
Bool found = FALSE;
|
||||
Res res;
|
||||
|
||||
AVER_CRITICAL(FALSE); /* TODO: this code is completely untested! */
|
||||
AVER_CRITICAL(foundReturn != NULL);
|
||||
AVER_CRITICAL(rangeReturn != NULL);
|
||||
AVER_CRITICAL(oldRangeReturn != NULL);
|
||||
/* AVERT_CRITICAL(ZoneSet, zoneSet); */
|
||||
AVERT_CRITICAL(Bool, high);
|
||||
|
||||
/* <design/failover#.impl.assume.flush>. */
|
||||
(void)LandFlush(fo->primary, fo->secondary);
|
||||
|
||||
res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->primary, size, zoneSet, high);
|
||||
if (res != ResOK || !found)
|
||||
res = LandFindInZones(&found, rangeReturn, oldRangeReturn, fo->secondary, size, zoneSet, high);
|
||||
|
||||
*foundReturn = found;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static Res failoverDescribe(Inst inst, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
Land land = CouldBeA(Land, inst);
|
||||
Failover fo = CouldBeA(Failover, land);
|
||||
LandClass primaryClass, secondaryClass;
|
||||
Res res;
|
||||
|
||||
if (!TESTC(Failover, fo))
|
||||
return ResPARAM;
|
||||
if (stream == NULL)
|
||||
return ResPARAM;
|
||||
|
||||
res = NextMethod(Inst, Failover, describe)(inst, stream, depth);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
primaryClass = ClassOfPoly(Land, fo->primary);
|
||||
secondaryClass = ClassOfPoly(Land, fo->secondary);
|
||||
|
||||
return WriteF(stream, depth + 2,
|
||||
"primary = $P ($S)\n",
|
||||
(WriteFP)fo->primary,
|
||||
(WriteFS)ClassName(primaryClass),
|
||||
"secondary = $P ($S)\n",
|
||||
(WriteFP)fo->secondary,
|
||||
(WriteFS)ClassName(secondaryClass),
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
DEFINE_CLASS(Land, Failover, klass)
|
||||
{
|
||||
INHERIT_CLASS(klass, Failover, Land);
|
||||
klass->instClassStruct.describe = failoverDescribe;
|
||||
klass->instClassStruct.finish = failoverFinish;
|
||||
klass->size = sizeof(FailoverStruct);
|
||||
klass->init = failoverInit;
|
||||
klass->sizeMethod = failoverSize;
|
||||
klass->insert = failoverInsert;
|
||||
klass->insertSteal = failoverInsertSteal;
|
||||
klass->delete = failoverDelete;
|
||||
klass->deleteSteal = failoverDeleteSteal;
|
||||
klass->iterate = failoverIterate;
|
||||
klass->findFirst = failoverFindFirst;
|
||||
klass->findLast = failoverFindLast;
|
||||
klass->findLargest = failoverFindLargest;
|
||||
klass->findInZones = failoverFindInZones;
|
||||
AVERT(LandClass, klass);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2014-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
61
mps/code/failover.h
Normal file
61
mps/code/failover.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* failover.h: FAILOVER ALLOCATOR INTERFACE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2014-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .source: <design/failover>.
|
||||
*/
|
||||
|
||||
#ifndef failover_h
|
||||
#define failover_h
|
||||
|
||||
#include "mpmtypes.h"
|
||||
#include "mpm.h"
|
||||
#include "protocol.h"
|
||||
|
||||
typedef struct FailoverStruct *Failover;
|
||||
|
||||
#define FailoverLand(fo) (&(fo)->landStruct)
|
||||
|
||||
extern Bool FailoverCheck(Failover failover);
|
||||
|
||||
DECLARE_CLASS(Land, Failover, Land);
|
||||
|
||||
extern const struct mps_key_s _mps_key_failover_primary;
|
||||
#define FailoverPrimary (&_mps_key_failover_primary)
|
||||
#define FailoverPrimary_FIELD p
|
||||
extern const struct mps_key_s _mps_key_failover_secondary;
|
||||
#define FailoverSecondary (&_mps_key_failover_secondary)
|
||||
#define FailoverSecondary_FIELD p
|
||||
|
||||
#endif /* failover.h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2014-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
278
mps/code/finalcv.c
Normal file
278
mps/code/finalcv.c
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/* finalcv.c: FINALIZATION COVERAGE TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* <design/poolmrg#.test>.
|
||||
*
|
||||
* DEPENDENCIES
|
||||
*
|
||||
* This test uses the dylan object format, but the reliance on this
|
||||
* particular format is not great and could be removed.
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* This code was created by first copying <code/weakcv.c>
|
||||
*/
|
||||
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mpm.h"
|
||||
#include "mps.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpscams.h"
|
||||
#include "mpscawl.h"
|
||||
#include "mpsclo.h"
|
||||
#include "mpslib.h"
|
||||
#include "mpstd.h"
|
||||
#include "testlib.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
|
||||
|
||||
#define testArenaSIZE ((size_t)16<<20)
|
||||
#define rootCOUNT 20
|
||||
#define churnFACTOR 10
|
||||
#define finalizationRATE 6
|
||||
#define gcINTERVAL ((size_t)150 * 1024)
|
||||
#define collectionCOUNT 3
|
||||
#define finalizationCOUNT 3
|
||||
|
||||
/* 3 words: wrapper | vector-len | first-slot */
|
||||
#define vectorSIZE (3*sizeof(mps_word_t))
|
||||
#define vectorSLOT 2
|
||||
|
||||
#define genCOUNT 2
|
||||
|
||||
/* testChain -- generation parameters for the test */
|
||||
|
||||
static mps_gen_param_s testChain[genCOUNT] = {
|
||||
{ 150, 0.85 }, { 170, 0.45 } };
|
||||
|
||||
|
||||
/* tags an integer according to dylan format */
|
||||
static mps_word_t dylan_int(mps_word_t x)
|
||||
{
|
||||
return (x << 2)|1;
|
||||
}
|
||||
|
||||
|
||||
/* converts a dylan format int to an int (untags) */
|
||||
static mps_word_t dylan_int_int(mps_word_t x)
|
||||
{
|
||||
return x >> 2;
|
||||
}
|
||||
|
||||
|
||||
/* note: static, so auto-initialised to NULL */
|
||||
static void *root[rootCOUNT];
|
||||
|
||||
|
||||
/* churn -- allocate a lot of stuff (unreachable garbage, so it will */
|
||||
/* probably only ever cause a minor collection). */
|
||||
static void churn(mps_ap_t ap)
|
||||
{
|
||||
int i;
|
||||
mps_addr_t p;
|
||||
mps_res_t e;
|
||||
|
||||
for (i = 0; i < churnFACTOR; ++i) {
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(e, p, ap, 4096);
|
||||
die(e, "MPS_RESERVE_BLOCK");
|
||||
die(dylan_init(p, 4096, root, 1), "dylan_init");
|
||||
} while (!mps_commit(ap, p, 4096));
|
||||
}
|
||||
p = NULL;
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
rootSTATE,
|
||||
deadSTATE,
|
||||
finalizableSTATE,
|
||||
finalizedSTATE
|
||||
};
|
||||
|
||||
|
||||
static void test(mps_arena_t arena, mps_pool_class_t pool_class)
|
||||
{
|
||||
size_t i; /* index */
|
||||
mps_ap_t ap;
|
||||
mps_fmt_t fmt;
|
||||
mps_chain_t chain;
|
||||
mps_pool_t pool;
|
||||
mps_res_t e;
|
||||
mps_root_t mps_root[2];
|
||||
mps_addr_t nullref = NULL;
|
||||
int state[rootCOUNT];
|
||||
size_t finalizations = 0;
|
||||
size_t collections = 0;
|
||||
void *p;
|
||||
|
||||
printf("---- finalcv: pool class %s ----\n", ClassName(pool_class));
|
||||
|
||||
die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n");
|
||||
die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create");
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt);
|
||||
die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create\n");
|
||||
} MPS_ARGS_END(args);
|
||||
die(mps_root_create_table(&mps_root[0], arena, mps_rank_exact(), (mps_rm_t)0,
|
||||
root, (size_t)rootCOUNT),
|
||||
"root_create\n");
|
||||
die(mps_root_create_table(&mps_root[1], arena, mps_rank_exact(), (mps_rm_t)0,
|
||||
&p, (size_t)1),
|
||||
"root_create\n");
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n");
|
||||
|
||||
/* Make registered-for-finalization objects. */
|
||||
/* <design/poolmrg#.test.promise.ut.alloc> */
|
||||
for(i = 0; i < rootCOUNT; ++i) {
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(e, p, ap, vectorSIZE);
|
||||
die(e, "MPS_RES_OK");
|
||||
die(dylan_init(p, vectorSIZE, &nullref, 1), "dylan_init");
|
||||
} while (!mps_commit(ap, p, vectorSIZE));
|
||||
|
||||
/* store index in vector's slot */
|
||||
((mps_word_t *)p)[vectorSLOT] = dylan_int(i);
|
||||
|
||||
/* mps_definalize fails when there have been no calls to mps_finalize
|
||||
yet, or for an address that was not registered for finalization. */
|
||||
Insist(mps_definalize(arena, &p) == MPS_RES_FAIL);
|
||||
|
||||
die(mps_finalize(arena, &p), "finalize\n");
|
||||
root[i] = p; state[i] = rootSTATE;
|
||||
}
|
||||
p = NULL;
|
||||
|
||||
mps_message_type_enable(arena, mps_message_type_finalization());
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
|
||||
/* <design/poolmrg#.test.promise.ut.churn> */
|
||||
while (finalizations < finalizationCOUNT && collections < collectionCOUNT) {
|
||||
mps_message_type_t type;
|
||||
|
||||
/* Perhaps cause (minor) collection */
|
||||
churn(ap);
|
||||
|
||||
/* Maybe make some objects ready-to-finalize */
|
||||
/* <design/poolmrg#.test.promise.ut.drop> */
|
||||
for (i = 0; i < rootCOUNT; ++i) {
|
||||
if (root[i] != NULL && state[i] == rootSTATE) {
|
||||
if (rnd() % finalizationRATE == 0) {
|
||||
/* for this object, either... */
|
||||
if (rnd() % 2 == 0) {
|
||||
/* ...definalize it, or */
|
||||
die(mps_definalize(arena, &root[i]), "definalize\n");
|
||||
state[i] = deadSTATE;
|
||||
} else {
|
||||
/* ...expect it to be finalized soon */
|
||||
state[i] = finalizableSTATE;
|
||||
}
|
||||
/* Drop the root reference to it; this makes it */
|
||||
/* non-E-reachable: so either dead, or ready-to-finalize. */
|
||||
root[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (mps_message_queue_type(&type, arena)) {
|
||||
mps_message_t message;
|
||||
cdie(mps_message_get(&message, arena, type), "message_get");
|
||||
if (type == mps_message_type_finalization()) {
|
||||
/* Check finalized object, and perhaps resurrect it. */
|
||||
mps_word_t *obj;
|
||||
mps_word_t objind;
|
||||
mps_addr_t objaddr;
|
||||
|
||||
/* <design/poolmrg#.test.promise.ut.message> */
|
||||
cdie(0 == mps_message_clock(arena, message),
|
||||
"message clock should be 0 (unset) for finalization messages");
|
||||
mps_message_finalization_ref(&objaddr, arena, message);
|
||||
obj = objaddr;
|
||||
objind = dylan_int_int(obj[vectorSLOT]);
|
||||
printf("Finalizing: object %"PRIuLONGEST" at %p\n",
|
||||
(ulongest_t)objind, objaddr);
|
||||
/* <design/poolmrg#.test.promise.ut.final.check> */
|
||||
cdie(root[objind] == NULL, "finalized live");
|
||||
cdie(state[objind] == finalizableSTATE, "finalized dead");
|
||||
state[objind] = finalizedSTATE;
|
||||
/* sometimes resurrect */
|
||||
if (rnd() % 2 == 0)
|
||||
root[objind] = objaddr;
|
||||
++ finalizations;
|
||||
} else if (type == mps_message_type_gc()) {
|
||||
++ collections;
|
||||
} else {
|
||||
error("Unexpected message type %lu.", (unsigned long)type);
|
||||
}
|
||||
mps_message_discard(arena, message);
|
||||
}
|
||||
}
|
||||
|
||||
mps_ap_destroy(ap);
|
||||
mps_root_destroy(mps_root[1]);
|
||||
mps_root_destroy(mps_root[0]);
|
||||
mps_pool_destroy(pool);
|
||||
mps_chain_destroy(chain);
|
||||
mps_fmt_destroy(fmt);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"arena_create\n");
|
||||
|
||||
test(arena, mps_class_amc());
|
||||
test(arena, mps_class_amcz());
|
||||
test(arena, mps_class_awl());
|
||||
test(arena, mps_class_ams());
|
||||
test(arena, mps_class_lo());
|
||||
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
350
mps/code/finaltest.c
Normal file
350
mps/code/finaltest.c
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
/* finaltest.c: LARGE-SCALE FINALIZATION TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* .mode: This test has two modes.
|
||||
*
|
||||
* .mode.park: In this mode, we use the arena's default generation
|
||||
* chain, leave the arena parked and call mps_arena_collect. This
|
||||
* tests that the default generation chain works and that all segments
|
||||
* get condemned via TraceStartCollectAll. (See job003771 item 4.)
|
||||
*
|
||||
* .mode.poll: In this mode, we use our own generation chain (with
|
||||
* small generations), allocate into generation 1, unclamp the arena,
|
||||
* and provoke collection by allocating. This tests that custom
|
||||
* generation chains work, and that segments get condemned via
|
||||
* TracePoll even if there is no allocation into generation 0 of the
|
||||
* chain. (See job003771 item 5.)
|
||||
*
|
||||
* DEPENDENCIES
|
||||
*
|
||||
* This test uses the dylan object format, but the reliance on this
|
||||
* particular format is not great and could be removed.
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* This code was created by first copying <code/finalcv.c>
|
||||
*/
|
||||
|
||||
#include "mpm.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
#include "mps.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpscams.h"
|
||||
#include "mpscawl.h"
|
||||
#include "mpsclo.h"
|
||||
#include "mpsavm.h"
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mpstd.h"
|
||||
|
||||
#include <math.h> /* HUGE_VAL */
|
||||
#include <stdio.h> /* fflush, printf, stdout */
|
||||
|
||||
enum {
|
||||
ModePARK, /* .mode.park */
|
||||
ModePOLL /* .mode.poll */
|
||||
};
|
||||
|
||||
|
||||
#define testArenaSIZE ((size_t)16<<20)
|
||||
#define rootCOUNT 20
|
||||
#define maxtreeDEPTH 9
|
||||
#define collectionCOUNT 10
|
||||
|
||||
|
||||
/* global object counter */
|
||||
|
||||
static mps_word_t object_count = 0;
|
||||
|
||||
static mps_word_t make_numbered_cons(mps_word_t car, mps_word_t cdr,
|
||||
mps_ap_t ap)
|
||||
{
|
||||
mps_word_t cons;
|
||||
die(make_dylan_vector(&cons, ap, 3), "make_dylan_vector");
|
||||
DYLAN_VECTOR_SLOT(cons, 0) = car;
|
||||
DYLAN_VECTOR_SLOT(cons, 1) = cdr;
|
||||
DYLAN_VECTOR_SLOT(cons, 2) = DYLAN_INT(object_count);
|
||||
++ object_count;
|
||||
return cons;
|
||||
}
|
||||
|
||||
static mps_word_t make_numbered_tree(mps_word_t depth,
|
||||
mps_ap_t ap)
|
||||
{
|
||||
mps_word_t left, right;
|
||||
if (depth < 2) {
|
||||
left = DYLAN_INT(object_count);
|
||||
right = DYLAN_INT(object_count);
|
||||
} else {
|
||||
left = make_numbered_tree(depth-1, ap);
|
||||
right = make_numbered_tree(depth-1, ap);
|
||||
}
|
||||
return make_numbered_cons(left, right, ap);
|
||||
}
|
||||
|
||||
static void register_numbered_tree(mps_word_t tree, mps_arena_t arena)
|
||||
{
|
||||
/* don't finalize ints */
|
||||
if ((tree & 1) == 0) {
|
||||
mps_addr_t tree_ref = (mps_addr_t)tree;
|
||||
die(mps_finalize(arena, &tree_ref), "mps_finalize");
|
||||
register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 0), arena);
|
||||
register_numbered_tree(DYLAN_VECTOR_SLOT(tree, 1), arena);
|
||||
}
|
||||
}
|
||||
|
||||
static mps_word_t make_indirect_cons(mps_word_t car, mps_word_t cdr,
|
||||
mps_ap_t ap)
|
||||
{
|
||||
mps_word_t cons, indirect;
|
||||
die(make_dylan_vector(&indirect, ap, 1), "make_dylan_vector");
|
||||
DYLAN_VECTOR_SLOT(indirect, 0) = DYLAN_INT(object_count);
|
||||
die(make_dylan_vector(&cons, ap, 3), "make_dylan_vector");
|
||||
DYLAN_VECTOR_SLOT(cons, 0) = car;
|
||||
DYLAN_VECTOR_SLOT(cons, 1) = cdr;
|
||||
DYLAN_VECTOR_SLOT(cons, 2) = indirect;
|
||||
++ object_count;
|
||||
return cons;
|
||||
}
|
||||
|
||||
static mps_word_t make_indirect_tree(mps_word_t depth,
|
||||
mps_ap_t ap)
|
||||
{
|
||||
mps_word_t left, right;
|
||||
if (depth < 2) {
|
||||
left = DYLAN_INT(object_count);
|
||||
right = DYLAN_INT(object_count);
|
||||
} else {
|
||||
left = make_indirect_tree(depth-1, ap);
|
||||
right = make_indirect_tree(depth-1, ap);
|
||||
}
|
||||
return make_indirect_cons(left, right, ap);
|
||||
}
|
||||
|
||||
static void register_indirect_tree(mps_word_t tree, mps_arena_t arena)
|
||||
{
|
||||
/* don't finalize ints */
|
||||
if ((tree & 1) == 0) {
|
||||
mps_word_t indirect = DYLAN_VECTOR_SLOT(tree,2);
|
||||
mps_addr_t indirect_ref = (mps_addr_t)indirect;
|
||||
die(mps_finalize(arena, &indirect_ref), "mps_finalize");
|
||||
register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 0), arena);
|
||||
register_indirect_tree(DYLAN_VECTOR_SLOT(tree, 1), arena);
|
||||
}
|
||||
}
|
||||
|
||||
static void *root[rootCOUNT];
|
||||
|
||||
static void test_trees(int mode, const char *name, mps_arena_t arena,
|
||||
mps_pool_t pool, mps_ap_t ap,
|
||||
mps_word_t (*make)(mps_word_t, mps_ap_t),
|
||||
void (*reg)(mps_word_t, mps_arena_t))
|
||||
{
|
||||
size_t collections = 0;
|
||||
size_t finals = 0;
|
||||
size_t i;
|
||||
int object_alloc;
|
||||
PoolClass klass = ClassOfPoly(Pool, pool);
|
||||
|
||||
object_count = 0;
|
||||
|
||||
printf("---- Mode %s, pool class %s, %s trees ----\n",
|
||||
mode == ModePARK ? "PARK" : "POLL",
|
||||
ClassName(klass), name);
|
||||
mps_arena_park(arena);
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
|
||||
/* make some trees */
|
||||
for(i = 0; i < rootCOUNT; ++i) {
|
||||
root[i] = (void *)(*make)(maxtreeDEPTH, ap);
|
||||
(*reg)((mps_word_t)root[i], arena);
|
||||
}
|
||||
|
||||
/* clean out the roots */
|
||||
for(i = 0; i < rootCOUNT; ++i) {
|
||||
root[i] = 0;
|
||||
}
|
||||
|
||||
while (finals < object_count && collections < collectionCOUNT) {
|
||||
mps_message_type_t type;
|
||||
mps_word_t final_this_time = 0;
|
||||
switch (mode) {
|
||||
default:
|
||||
case ModePARK:
|
||||
printf("Collecting...");
|
||||
(void)fflush(stdout);
|
||||
die(mps_arena_collect(arena), "collect");
|
||||
printf(" Done.\n");
|
||||
break;
|
||||
case ModePOLL:
|
||||
mps_arena_release(arena);
|
||||
printf("Allocating...");
|
||||
(void)fflush(stdout);
|
||||
object_alloc = 0;
|
||||
while (object_alloc < 1000 && !mps_message_poll(arena))
|
||||
(void)DYLAN_INT(object_alloc++);
|
||||
printf(" Done.\n");
|
||||
break;
|
||||
}
|
||||
{
|
||||
size_t live_size = (object_count - finals) * sizeof(void *) * 3;
|
||||
size_t total_size = mps_pool_total_size(pool);
|
||||
size_t free_size = mps_pool_free_size(pool);
|
||||
Insist(free_size <= total_size);
|
||||
Insist(free_size + live_size <= total_size);
|
||||
}
|
||||
while (mps_message_queue_type(&type, arena)) {
|
||||
mps_message_t message;
|
||||
cdie(mps_message_get(&message, arena, type), "message_get");
|
||||
if (type == mps_message_type_finalization()) {
|
||||
mps_addr_t objaddr;
|
||||
mps_message_finalization_ref(&objaddr, arena, message);
|
||||
++ final_this_time;
|
||||
} else if (type == mps_message_type_gc()) {
|
||||
++ collections;
|
||||
} else {
|
||||
error("Unexpected message type %lu.", (unsigned long)type);
|
||||
}
|
||||
mps_message_discard(arena, message);
|
||||
}
|
||||
finals += final_this_time;
|
||||
printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST
|
||||
" of %"PRIuLONGEST"\n", (ulongest_t)final_this_time,
|
||||
(ulongest_t)finals, (ulongest_t)object_count);
|
||||
}
|
||||
|
||||
if (finals != object_count) {
|
||||
PoolClass poolClass = ClassOfPoly(Pool, BufferOfAP(ap)->pool);
|
||||
error("Not all objects were finalized for %s in mode %s.",
|
||||
ClassName(poolClass),
|
||||
mode == ModePOLL ? "POLL" : "PARK");
|
||||
}
|
||||
|
||||
if (collections > collectionCOUNT)
|
||||
error("Expected no more than %lu collections but got %lu.",
|
||||
(unsigned long)collectionCOUNT, (unsigned long)collections);
|
||||
}
|
||||
|
||||
static void test_pool(int mode, mps_arena_t arena, mps_chain_t chain,
|
||||
mps_pool_class_t pool_class)
|
||||
{
|
||||
mps_ap_t ap;
|
||||
mps_fmt_t fmt;
|
||||
mps_pool_t pool;
|
||||
mps_root_t mps_root;
|
||||
|
||||
die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n");
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt);
|
||||
if (mode == ModePOLL) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_GEN, 1);
|
||||
}
|
||||
die(mps_pool_create_k(&pool, arena, pool_class, args),
|
||||
"pool_create\n");
|
||||
} MPS_ARGS_END(args);
|
||||
die(mps_root_create_table(&mps_root, arena, mps_rank_exact(), (mps_rm_t)0,
|
||||
root, (size_t)rootCOUNT),
|
||||
"root_create\n");
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n");
|
||||
|
||||
test_trees(mode, "numbered", arena, pool, ap, make_numbered_tree,
|
||||
register_numbered_tree);
|
||||
test_trees(mode, "indirect", arena, pool, ap, make_indirect_tree,
|
||||
register_indirect_tree);
|
||||
|
||||
mps_ap_destroy(ap);
|
||||
mps_root_destroy(mps_root);
|
||||
mps_pool_destroy(pool);
|
||||
mps_fmt_destroy(fmt);
|
||||
}
|
||||
|
||||
|
||||
static void test_mode(int mode, mps_arena_t arena, mps_chain_t chain)
|
||||
{
|
||||
test_pool(mode, arena, chain, mps_class_amc());
|
||||
test_pool(mode, arena, chain, mps_class_amcz());
|
||||
test_pool(mode, arena, chain, mps_class_ams());
|
||||
test_pool(mode, arena, chain, mps_class_awl());
|
||||
test_pool(mode, arena, chain, mps_class_lo());
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_thr_t thread;
|
||||
mps_chain_t chain;
|
||||
mps_gen_param_s params[2];
|
||||
size_t gens = 2;
|
||||
size_t i;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
/* Randomize pause time as a regression test for job004007. */
|
||||
double t = rnd_double();
|
||||
if (t == 0.0)
|
||||
t = HUGE_VAL; /* Would prefer to use INFINITY but it's not in C89. */
|
||||
else
|
||||
t = 1 / t - 1;
|
||||
MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, t);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE);
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args),
|
||||
"arena_create\n");
|
||||
} MPS_ARGS_END(args);
|
||||
mps_message_type_enable(arena, mps_message_type_finalization());
|
||||
die(mps_thread_reg(&thread, arena), "thread_reg\n");
|
||||
for (i = 0; i < gens; ++i) {
|
||||
params[i].mps_capacity = 1;
|
||||
params[i].mps_mortality = 0.5;
|
||||
}
|
||||
die(mps_chain_create(&chain, arena, gens, params), "chain_create\n");
|
||||
|
||||
test_mode(ModePOLL, arena, chain);
|
||||
test_mode(ModePARK, arena, NULL);
|
||||
|
||||
mps_arena_park(arena);
|
||||
mps_chain_destroy(chain);
|
||||
mps_thread_dereg(thread);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
886
mps/code/fmtdy.c
Normal file
886
mps/code/fmtdy.c
Normal file
|
|
@ -0,0 +1,886 @@
|
|||
/* fmtdy.c: DYLAN OBJECT FORMAT IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (c) 2002 Global Graphics Software.
|
||||
*
|
||||
* .readership: MPS developers, Dylan developers
|
||||
*
|
||||
* .layouts:
|
||||
*
|
||||
* All objects, B:
|
||||
*
|
||||
* B W pointer to wrapper
|
||||
* B+1 object body
|
||||
*
|
||||
* Forwarded (or padding) one-word objects, B:
|
||||
*
|
||||
* B N | 0b01 new address | 1
|
||||
*
|
||||
* Forwarded (or padding) multi-word objects, B:
|
||||
*
|
||||
* B N | 0b10 new address | 2
|
||||
* B+1 L limit of object (addr of end + 1)
|
||||
*
|
||||
* Wrappers, W:
|
||||
*
|
||||
* W WW pointer to wrapper wrapper
|
||||
* W+1 class DylanWorks class pointer (traceable)
|
||||
* W+2 subtype_mask DylanWorks subtype_mask (untraceable)
|
||||
* W+3 (FL << 2) | FF fixed part length and format
|
||||
* W+4 (VS << 3) | VF variable part format and element size
|
||||
* W+5 (WT << 2) | 1 tagged pattern vector length
|
||||
* W+6 pattern 0 patterns for fixed part fields
|
||||
* W+6+WT-1 pattern WT-1
|
||||
*
|
||||
* The wrapper wrapper, WW:
|
||||
*
|
||||
* WW WW WW is it's own wrapper
|
||||
* WW+1 class DylanWorks class of wrappers
|
||||
* WW+2 subtype_mask DylanWorks subtype_mask for WW
|
||||
* WW+3 (4 << 2) | 2 wrappers have four patterned fields
|
||||
* WW+4 (0 << 3) | 0 wrappers have a non-traceable vector
|
||||
* WW+5 (1 << 2) | 1 one pattern word follows
|
||||
* WW+6 0b001 only field 0 is traceable
|
||||
*
|
||||
* .improve.abstract.access: There are severe common subexpression
|
||||
* problems. In particular, code for accessing subfields in the
|
||||
* fh and vh words is repeated. It should be abstracted into
|
||||
* macros (or functions). This is particularly bad for the vh
|
||||
* word which has 4 subfields (version, vb, es, vf).
|
||||
*/
|
||||
|
||||
|
||||
#include "fmtdy.h"
|
||||
#include "fmtno.h"
|
||||
#include "mps.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
#define notreached() assert(0)
|
||||
#define unused(param) ((void)param)
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
/* MPS_END causes "constant conditional" warnings. */
|
||||
#pragma warning(disable: 4127)
|
||||
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
|
||||
#define ALIGN sizeof(mps_word_t)
|
||||
|
||||
#define FMTDY_WORD_WIDTH (sizeof(mps_word_t) * CHAR_BIT)
|
||||
#define FMTDY_WORD_SHIFT (FMTDY_WORD_WIDTH == 64 ? 6 : 5)
|
||||
/* FMTDY_WORD_SHIFT is a bit hacky, but good enough for tests. */
|
||||
|
||||
#ifdef FMTDY_COUNTING
|
||||
#define FMTDY_COUNT(x) x
|
||||
#define FMTDY_FL_LIMIT 16
|
||||
static unsigned long dylan_vff_counts[4*8];
|
||||
static unsigned long dylan_fl_counts[FMTDY_FL_LIMIT];
|
||||
static unsigned long dylan_fl_oversize_count;
|
||||
static unsigned long dylan_fw_counts[2];
|
||||
#else
|
||||
#define FMTDY_COUNT(x)
|
||||
#endif /* FMTDY_COUNTING */
|
||||
|
||||
|
||||
int dylan_wrapper_check(mps_word_t *w)
|
||||
{
|
||||
mps_word_t *ww;
|
||||
mps_word_t vh;
|
||||
mps_word_t version;
|
||||
mps_word_t reserved;
|
||||
mps_word_t klass;
|
||||
mps_word_t fh, fl, ff;
|
||||
mps_word_t vb, es, vf;
|
||||
mps_word_t vt, t;
|
||||
|
||||
assert(w != NULL);
|
||||
assert(((mps_word_t)w & 3) == 0);
|
||||
|
||||
/* The first word of the wrapper is a pointer to a wrapper wrapper, */
|
||||
/* which always has the same contents. Check it. */
|
||||
|
||||
/* .improve.unique.wrapper: When this becomes part of the Dylan
|
||||
* run-time, it would be possible to know the address of a unique
|
||||
* wrapper wrapper and check that instead. */
|
||||
|
||||
assert(w[WW] != 0);
|
||||
assert((w[WW] & 3) == 0); /* wrapper wrapper is aligned */
|
||||
ww = (mps_word_t *)w[WW];
|
||||
assert(ww[WW] == w[WW]); /* wrapper wrapper is own wrapper */
|
||||
assert(ww[WC] != 0); /* wrapper class exists */
|
||||
assert((ww[WC] & 3) == 0); /* wrapper class is aligned */
|
||||
assert(ww[WF] == (((WS - 1) << 2) | 2)); /* fields with patterns */
|
||||
assert((ww[WV] & 0x00ffffff) == 0);/* non-traceable vector */
|
||||
/* Code in this file only works for version 2 */
|
||||
assert(((ww[WV] >> (FMTDY_WORD_WIDTH - 8)) & 0xff) == 2);
|
||||
assert(ww[WS] == ((1 << 2) | 1)); /* one pattern word in wrapper wrapper */
|
||||
/* The first field is traceable, the second field can be traced, */
|
||||
/* but doesn't need to be. */
|
||||
assert((ww[WP] == 1) || (ww[WP] == 3));
|
||||
unused(ww);
|
||||
|
||||
/* Unpack the wrapper. */
|
||||
|
||||
klass = w[WC]; /* class */
|
||||
unused(klass);
|
||||
fh = w[WF]; /* fixed part header word */
|
||||
fl = fh >> 2; /* fixed part length */
|
||||
ff = fh & 3; /* fixed part format code */
|
||||
vh = w[WV]; /* variable part header */
|
||||
version = (vh >> (FMTDY_WORD_WIDTH - 8)) & 0xff;
|
||||
assert(version == 2); /* Code in this file only works for version 2 */
|
||||
unused(version);
|
||||
reserved = (vh >> 8) & 0xff;
|
||||
assert(reserved == 0);
|
||||
unused(reserved);
|
||||
vb = (vh >> 16) & 0xff;
|
||||
unused(vb);
|
||||
es = (vh & 0xff) >> 3;/* element size */
|
||||
vf = vh & 7; /* variable part format code */
|
||||
vt = w[WS]; /* vector total word (Dylan-tagged) */
|
||||
t = vt >> 2; /* vector total length */
|
||||
unused(t);
|
||||
|
||||
/* The second word is the class of the wrapped object. */
|
||||
/* It would be good to check which pool this is in. */
|
||||
|
||||
assert(klass != 0); /* class exists */
|
||||
assert((klass & 3) == 0); /* class is aligned */
|
||||
|
||||
/* The third word contains the fixed part format and length. */
|
||||
/* The only illegal format is 3. Anything else is possible, although */
|
||||
/* we could do some bound checking on the length if we knew more about */
|
||||
/* the surroundings of the object. */
|
||||
|
||||
/* Fixed part format 3 is reserved. */
|
||||
assert(ff != 3);
|
||||
unused(ff);
|
||||
|
||||
/* Zero length fixed part is only legal in format 0. */
|
||||
/* Current Dylan run-time does not honour this so I remove it for now */
|
||||
/* We probably want this check as then we can scan without having to */
|
||||
/* check for 0 fixed length fields as a special case */
|
||||
/* assert(ff == 0 || fl != 0); */
|
||||
unused(fl);
|
||||
/* The fourth word contains the variable part format and element */
|
||||
/* size. This assumes that DylanWorks is only going to use byte */
|
||||
/* vectors in the non-word case. */
|
||||
|
||||
/* Variable part format 6 is reserved. */
|
||||
assert(vf != 6);
|
||||
unused(vf);
|
||||
|
||||
/* There should be no shift in word vector formats. */
|
||||
assert((vf & 6) == 4 || es == 0);
|
||||
unused(es);
|
||||
|
||||
/* The fifth word is the number of patterns in the pattern */
|
||||
/* vector. This can be calculated from the fixed part length. */
|
||||
/* The word is also tagged like a DylanWorks integer. */
|
||||
|
||||
assert((vt & 3) == 1);
|
||||
|
||||
/* The pattern vector in the wrapper should be of non-zero length */
|
||||
/* only if there is a patterned fixed part. */
|
||||
assert(ff == 2 || t == 0);
|
||||
|
||||
/* The number of patterns is (fixed fields+31)/32. */
|
||||
assert(ff != 2 || t == ((fl + FMTDY_WORD_WIDTH - 1) / FMTDY_WORD_WIDTH));
|
||||
|
||||
/* The patterns are random bits, so we can't check them. However, */
|
||||
/* the left-over bits in the last pattern should be zero. */
|
||||
|
||||
assert(ff != 2 || (w[WS+t] >> ((fh>>2) & (FMTDY_WORD_WIDTH-1))) == 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Scan a contiguous array of references in [base, limit). */
|
||||
/* This code has been hand-optimised and examined using Metrowerks */
|
||||
/* Codewarrior on a 68K and also Microsoft Visual C on a 486. The */
|
||||
/* variables in the loop allocate nicely into registers. Alter with */
|
||||
/* care. */
|
||||
|
||||
static mps_res_t dylan_scan_contig(mps_ss_t mps_ss,
|
||||
mps_addr_t *base, mps_addr_t *limit)
|
||||
{
|
||||
mps_res_t res;
|
||||
mps_addr_t *p; /* reference cursor */
|
||||
mps_addr_t r; /* reference to be fixed */
|
||||
|
||||
MPS_SCAN_BEGIN(mps_ss) {
|
||||
p = base;
|
||||
loop: if(p >= limit) goto out;
|
||||
r = *p++;
|
||||
if(((mps_word_t)r&3) != 0) /* pointers tagged with 0 */
|
||||
goto loop; /* not a pointer */
|
||||
if(!MPS_FIX1(mps_ss, r)) goto loop;
|
||||
res = MPS_FIX2(mps_ss, p-1);
|
||||
if(res == MPS_RES_OK) goto loop;
|
||||
return res;
|
||||
out: assert(p == limit);
|
||||
} MPS_SCAN_END(mps_ss);
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
/* dylan_weak_dependent -- returns the linked object, if any.
|
||||
*/
|
||||
|
||||
mps_addr_t dylan_weak_dependent(mps_addr_t parent)
|
||||
{
|
||||
mps_word_t *object;
|
||||
mps_word_t *wrapper;
|
||||
mps_word_t fword;
|
||||
mps_word_t fl;
|
||||
mps_word_t ff;
|
||||
|
||||
assert(parent != NULL);
|
||||
object = (mps_word_t *)parent;
|
||||
wrapper = (mps_word_t *)object[0];
|
||||
assert(dylan_wrapper_check(wrapper));
|
||||
fword = wrapper[3];
|
||||
ff = fword & 3;
|
||||
/* traceable fixed part */
|
||||
assert(ff == 1);
|
||||
unused(ff);
|
||||
fl = fword & ~(mps_word_t)3;
|
||||
/* at least one fixed field */
|
||||
assert(fl >= 1);
|
||||
unused(fl);
|
||||
return (mps_addr_t) object[1];
|
||||
}
|
||||
|
||||
|
||||
/* Scan weakly a contiguous array of references in [base, limit). */
|
||||
/* Only required to scan vectors for Dylan Weak Tables. */
|
||||
/* Depends on the vector length field being scannable (ie a tagged */
|
||||
/* integer). */
|
||||
/* When a reference that has been fixed to NULL is detected the */
|
||||
/* corresponding reference in the associated table (pointed to be the */
|
||||
/* assoc variable) will be deleted. */
|
||||
|
||||
static mps_res_t
|
||||
dylan_scan_contig_weak(mps_ss_t mps_ss,
|
||||
mps_addr_t *base, mps_addr_t *limit,
|
||||
mps_addr_t *objectBase, mps_addr_t *assoc)
|
||||
{
|
||||
mps_addr_t *p;
|
||||
mps_res_t res;
|
||||
mps_addr_t r;
|
||||
|
||||
MPS_SCAN_BEGIN(mps_ss) {
|
||||
p = base;
|
||||
goto skip_inc;
|
||||
loop:
|
||||
++p;
|
||||
skip_inc:
|
||||
if(p >= limit)
|
||||
goto out;
|
||||
r = *p;
|
||||
if(((mps_word_t)r & 3) != 0) /* non-pointer */
|
||||
goto loop;
|
||||
if(!MPS_FIX1(mps_ss, r))
|
||||
goto loop;
|
||||
res = MPS_FIX2(mps_ss, p);
|
||||
if(res == MPS_RES_OK) {
|
||||
if(*p == 0 && r != 0) {
|
||||
if(assoc != NULL) {
|
||||
assoc[p-objectBase] = 0; /* delete corresponding entry */
|
||||
}
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
return res;
|
||||
out:
|
||||
assert(p == limit);
|
||||
} MPS_SCAN_END(mps_ss);
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
/* dylan_scan_pat -- scan according to pattern */
|
||||
|
||||
/* Scan an array of words in [base, limit) using the patterns at pats */
|
||||
/* to determine which words can be fixed. */
|
||||
/* This code has been hand-optimised and examined using Metrowerks */
|
||||
/* Codewarrior on a 68K and also Microsoft Visual C on a 486. The */
|
||||
/* variables in the loop allocate nicely into registers. Alter with */
|
||||
/* care. */
|
||||
|
||||
static mps_res_t dylan_scan_pat(mps_ss_t mps_ss,
|
||||
mps_addr_t *base, mps_addr_t *limit,
|
||||
mps_word_t *pats, mps_word_t nr_pats)
|
||||
{
|
||||
mps_res_t res;
|
||||
mps_word_t *pc = pats;/* pattern cursor */
|
||||
mps_word_t pat; /* pattern register */
|
||||
mps_addr_t *p; /* reference cursor */
|
||||
mps_addr_t *pp; /* inner loop cursor */
|
||||
int b; /* bit */
|
||||
mps_addr_t r; /* reference to be fixed */
|
||||
|
||||
unused(nr_pats);
|
||||
|
||||
MPS_SCAN_BEGIN(mps_ss) {
|
||||
p = base;
|
||||
goto in;
|
||||
pat: p += FMTDY_WORD_WIDTH;
|
||||
if(p >= limit) goto out;
|
||||
in: pp = p;
|
||||
pat = *pc++;
|
||||
loop: if(pat == 0) goto pat;
|
||||
++pp;
|
||||
b = (int)(pat & 1);
|
||||
pat >>= 1;
|
||||
if(b == 0) goto loop;
|
||||
r = *(pp-1);
|
||||
if(((mps_word_t)r&3) != 0) /* pointers tagged with 0 */
|
||||
goto loop; /* not a pointer */
|
||||
if(!MPS_FIX1(mps_ss, r)) goto loop;
|
||||
res = MPS_FIX2(mps_ss, pp-1);
|
||||
if(res == MPS_RES_OK) goto loop;
|
||||
return res;
|
||||
out: assert(p < limit + FMTDY_WORD_WIDTH);
|
||||
assert(pc == pats + nr_pats);
|
||||
} MPS_SCAN_END(mps_ss);
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
#define NONWORD_LENGTH(_vt, _es) \
|
||||
((_es) < FMTDY_WORD_SHIFT ? \
|
||||
((_vt) + ((mps_word_t)1 << (FMTDY_WORD_SHIFT - (_es))) - 1) >> \
|
||||
(FMTDY_WORD_SHIFT - (_es)) : \
|
||||
(_vt) << ((_es) - FMTDY_WORD_SHIFT))
|
||||
|
||||
|
||||
mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io)
|
||||
{
|
||||
mps_addr_t *p; /* cursor in object */
|
||||
mps_addr_t *q; /* cursor limit for loops */
|
||||
mps_word_t h; /* header word */
|
||||
mps_word_t *w; /* pointer to wrapper */
|
||||
mps_word_t fh; /* fixed part header word */
|
||||
mps_word_t fl; /* fixed part length, in words */
|
||||
mps_word_t vh; /* variable part header */
|
||||
mps_word_t vf; /* variable part format */
|
||||
mps_word_t vl; /* variable part actual length */
|
||||
unsigned vb; /* vector bias */
|
||||
unsigned es; /* variable part element size (log2 of bits) */
|
||||
mps_word_t vt; /* total vector length */
|
||||
mps_res_t res;
|
||||
|
||||
assert(object_io != NULL);
|
||||
|
||||
p = (mps_addr_t *)*object_io;
|
||||
assert(p != NULL);
|
||||
|
||||
h = (mps_word_t)p[0]; /* load the header word */
|
||||
|
||||
/* If the object is forwarded, simply skip it. */
|
||||
if(h & 3) {
|
||||
mps_addr_t l;
|
||||
|
||||
if((h & 3) == 1) {
|
||||
/* single-word */
|
||||
l = (mps_addr_t)(p + 1);
|
||||
FMTDY_COUNT(++dylan_fw_counts[0]);
|
||||
} else { /* multi-word */
|
||||
assert((h & 3) == 2);
|
||||
l = (mps_addr_t)p[1];
|
||||
FMTDY_COUNT(++dylan_fw_counts[1]);
|
||||
}
|
||||
|
||||
*object_io = l;
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
MPS_SCAN_BEGIN(mps_ss) {
|
||||
res = MPS_FIX12(mps_ss, p); /* fix the wrapper */
|
||||
} MPS_SCAN_END(mps_ss);
|
||||
if (res != MPS_RES_OK)
|
||||
return res;
|
||||
w = (mps_word_t *)p[0]; /* wrapper is header word */
|
||||
assert(dylan_wrapper_check(w));
|
||||
|
||||
++p; /* skip header */
|
||||
|
||||
/* Fixed Part */
|
||||
|
||||
fh = w[WF];
|
||||
fl = fh >> 2; /* get the fixed part length */
|
||||
|
||||
/* It might be worth inlining common cases here, for example, */
|
||||
/* pairs. This can be done by examining fh as a whole. */
|
||||
|
||||
FMTDY_COUNT(fl < FMTDY_FL_LIMIT ? ++dylan_fl_counts[fl] :
|
||||
++dylan_fl_oversize_count);
|
||||
if(fl > 0) {
|
||||
q = p + fl; /* set q to end of fixed part */
|
||||
switch(fh & 3) { /* switch on the fixed format */
|
||||
case 0: /* all non-traceable fields */
|
||||
p = q;
|
||||
break;
|
||||
|
||||
case 1: /* all traceable fields */
|
||||
res = dylan_scan_contig(mps_ss, p, q);
|
||||
if(res) return res;
|
||||
break;
|
||||
|
||||
case 2: /* patterns */
|
||||
res = dylan_scan_pat(mps_ss, p, q, &w[WP], w[WS]>>2);
|
||||
if(res) return res;
|
||||
break;
|
||||
|
||||
default:
|
||||
notreached();
|
||||
break;
|
||||
}
|
||||
p = q;
|
||||
}
|
||||
|
||||
/* Variable Part */
|
||||
vh = w[WV];
|
||||
vf = vh & 7; /* get variable part format */
|
||||
FMTDY_COUNT(++dylan_vff_counts[(vf << 2)|(fh&3)]);
|
||||
if(vf != 7)
|
||||
{
|
||||
vt = *(mps_word_t *)p; /* total vector length */
|
||||
assert((vt & 3) == 1); /* check Dylan integer tag */
|
||||
vt >>= 2; /* untag it */
|
||||
++p;
|
||||
|
||||
switch(vf)
|
||||
{
|
||||
case 0: /* non-stretchy non-traceable */
|
||||
p += vt;
|
||||
break;
|
||||
|
||||
case 1: /* stretchy non-traceable */
|
||||
p += vt + 1;
|
||||
notreached(); /* Not used by DylanWorks yet */
|
||||
break;
|
||||
|
||||
case 2: /* non-stretchy traceable */
|
||||
q = p + vt;
|
||||
res = dylan_scan_contig(mps_ss, p, q);
|
||||
if(res) return res;
|
||||
p = q;
|
||||
break;
|
||||
|
||||
case 3: /* stretchy traceable */
|
||||
vl = *(mps_word_t *)p; /* vector length */
|
||||
assert((vl & 3) == 1); /* check Dylan integer tag */
|
||||
vl >>= 2; /* untag it */
|
||||
++p;
|
||||
res = dylan_scan_contig(mps_ss, p, p + vl);
|
||||
if(res) return res;
|
||||
p += vt; /* skip to end of whole vector */
|
||||
notreached(); /* DW doesn't create them yet */
|
||||
break;
|
||||
|
||||
case 4: /* non-word */
|
||||
es = (unsigned)(vh & 0xff) >> 3;
|
||||
vb = (unsigned)((vh >> 16) & 0xff);
|
||||
vt += vb;
|
||||
p += NONWORD_LENGTH(vt, es);
|
||||
break;
|
||||
|
||||
case 5: /* stretchy non-word */
|
||||
es = (unsigned)(vh & 0xff) >> 3;
|
||||
vb = (unsigned)((vh >> 16) & 0xff);
|
||||
vt += vb;
|
||||
p += NONWORD_LENGTH(vt, es) + 1;
|
||||
notreached(); /* DW doesn't create them yet */
|
||||
break;
|
||||
|
||||
default:
|
||||
notreached();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*object_io = (mps_addr_t)p;
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
static mps_res_t dylan_scan(mps_ss_t mps_ss,
|
||||
mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
mps_res_t res;
|
||||
mps_addr_t prev = base;
|
||||
|
||||
while(base < limit) {
|
||||
prev = base;
|
||||
res = dylan_scan1(mps_ss, &base);
|
||||
if(res) return res;
|
||||
assert(prev < base);
|
||||
}
|
||||
|
||||
assert(base == limit);
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
/* dylan_class -- return pointer indicating class of object
|
||||
*
|
||||
* Return wrapper pointer, except for broken hearts or padding
|
||||
*/
|
||||
|
||||
static mps_addr_t dylan_class(mps_addr_t obj)
|
||||
{
|
||||
mps_word_t first_word = ((mps_word_t *)obj)[0];
|
||||
|
||||
if((first_word & 3) != 0) /* broken heart or padding */
|
||||
return NULL;
|
||||
else
|
||||
return (mps_addr_t)first_word;
|
||||
}
|
||||
|
||||
mps_res_t dylan_scan1_weak(mps_ss_t mps_ss, mps_addr_t *object_io)
|
||||
{
|
||||
mps_addr_t *assoc;
|
||||
mps_addr_t *base;
|
||||
mps_addr_t *p, q;
|
||||
mps_res_t res;
|
||||
mps_word_t *w;
|
||||
mps_word_t fword, ff, fl;
|
||||
mps_word_t h;
|
||||
mps_word_t vword, vf, vl;
|
||||
|
||||
assert(object_io != NULL);
|
||||
base = (mps_addr_t *)*object_io;
|
||||
assert(base != NULL);
|
||||
p = base;
|
||||
|
||||
h = (mps_word_t)p[0];
|
||||
/* object should not be forwarded (as there is no forwarding method) */
|
||||
assert((h & 3) == 0);
|
||||
unused(h);
|
||||
|
||||
MPS_SCAN_BEGIN(mps_ss) {
|
||||
res = MPS_FIX12(mps_ss, p);
|
||||
} MPS_SCAN_END(mps_ss);
|
||||
if (res != MPS_RES_OK)
|
||||
return res;
|
||||
|
||||
/* w points to wrapper */
|
||||
w = (mps_word_t *)p[0];
|
||||
|
||||
assert(dylan_wrapper_check(w));
|
||||
|
||||
++p; /* skip header */
|
||||
|
||||
fword = w[WF];
|
||||
fl = fword >> 2;
|
||||
/* weak vectors should have at least one fixed field */
|
||||
/* (for assoc field) */
|
||||
assert(fl >= 1);
|
||||
|
||||
ff = fword & 3;
|
||||
|
||||
/* weak vectors should have traceable fixed format */
|
||||
assert(ff == 1);
|
||||
unused(ff);
|
||||
|
||||
assoc = (mps_addr_t *)p[0];
|
||||
|
||||
vword = w[WV];
|
||||
vf = vword & 7;
|
||||
vl = (mps_word_t)p[fl] >> 2;
|
||||
|
||||
/* weak vectors should be non-stretchy traceable */
|
||||
assert(vf == 2);
|
||||
unused(vf);
|
||||
|
||||
/* q is end of the object. There are fl fixed fields, vl variable */
|
||||
/* fields and another slot that contains the vector length */
|
||||
q = p + fl + vl + 1;
|
||||
|
||||
res = dylan_scan_contig_weak(mps_ss, p, q, base, assoc);
|
||||
if(res != MPS_RES_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*object_io = q;
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
static mps_res_t dylan_scan_weak(mps_ss_t mps_ss,
|
||||
mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
mps_res_t res;
|
||||
|
||||
while(base < limit) {
|
||||
res = dylan_scan1_weak(mps_ss, &base);
|
||||
if(res) return res;
|
||||
}
|
||||
|
||||
assert(base == limit);
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
mps_addr_t dylan_skip(mps_addr_t object)
|
||||
{
|
||||
mps_addr_t *p; /* cursor in object */
|
||||
mps_word_t *w; /* wrapper cursor */
|
||||
mps_word_t h; /* header word */
|
||||
mps_word_t vh; /* variable part header */
|
||||
mps_word_t vf; /* variable part format */
|
||||
mps_word_t vt; /* total vector length */
|
||||
unsigned vb; /* vector bias */
|
||||
unsigned es; /* variable part element size (log2 of bits) */
|
||||
|
||||
p = (mps_addr_t *)object;
|
||||
assert(p != NULL);
|
||||
|
||||
h = (mps_word_t)p[0]; /* load the header word */
|
||||
|
||||
/* If the object is forwarded, simply skip it. */
|
||||
if(h & 3) {
|
||||
mps_addr_t l;
|
||||
|
||||
if((h & 3) == 1) /* single-word */
|
||||
l = (mps_addr_t)(p + 1);
|
||||
else { /* multi-word */
|
||||
assert((h & 3) == 2);
|
||||
l = (mps_addr_t)p[1];
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
w = (mps_word_t *)h; /* load the fixed wrapper */
|
||||
assert(dylan_wrapper_check(w));
|
||||
++p;
|
||||
|
||||
p += w[WF] >> 2; /* skip fixed part fields */
|
||||
|
||||
vh = w[WV];
|
||||
vf = vh & 7; /* get variable part format */
|
||||
if(vf != 7)
|
||||
{
|
||||
vt = *(mps_word_t *)p;
|
||||
assert((vt & 3) == 1); /* check Dylan integer tag */
|
||||
vt = vt >> 2; /* total length */
|
||||
++p;
|
||||
|
||||
p += vf & 1; /* stretchy vectors have an extra word */
|
||||
|
||||
if((vf & 6) == 4) /* non-word */
|
||||
{
|
||||
es = (unsigned)(vh & 0xff) >> 3;
|
||||
vb = (unsigned)((vh >> 16) & 0xff);
|
||||
vt += vb;
|
||||
p += NONWORD_LENGTH(vt, es);
|
||||
}
|
||||
else
|
||||
p += vt;
|
||||
}
|
||||
|
||||
return (mps_addr_t)p;
|
||||
}
|
||||
|
||||
static void dylan_copy(mps_addr_t old, mps_addr_t new)
|
||||
{
|
||||
char *base = (char *)old;
|
||||
char *limit = (char *)dylan_skip(old);
|
||||
size_t length;
|
||||
assert(base < limit);
|
||||
length = (size_t)(limit - base);
|
||||
assert(dylan_wrapper_check(*(mps_word_t **)old));
|
||||
/* .improve.memcpy: Can do better here as we know that new and old
|
||||
will be aligned (to MPS_PF_ALIGN) */
|
||||
(void)memcpy(new, old, length);
|
||||
}
|
||||
|
||||
static mps_addr_t dylan_isfwd(mps_addr_t object)
|
||||
{
|
||||
mps_word_t h, tag;
|
||||
|
||||
h = *(mps_word_t *)object;
|
||||
tag = h & 3;
|
||||
if(tag != 0)
|
||||
return (mps_addr_t)(h - tag);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dylan_fwd(mps_addr_t old, mps_addr_t new)
|
||||
{
|
||||
mps_word_t *p;
|
||||
mps_addr_t limit;
|
||||
|
||||
assert(dylan_isfwd(old) == NULL);
|
||||
assert((*(mps_word_t *)old & 3) == 0); /* mustn't forward padding objects */
|
||||
assert(((mps_word_t)new & 3) == 0);
|
||||
|
||||
p = (mps_word_t *)old;
|
||||
limit = dylan_skip(old);
|
||||
if(limit == &p[1]) /* single-word object? */
|
||||
p[0] = (mps_word_t)new | 1;
|
||||
else {
|
||||
p[0] = (mps_word_t)new | 2;
|
||||
p[1] = (mps_word_t)limit;
|
||||
}
|
||||
}
|
||||
|
||||
void dylan_pad(mps_addr_t addr, size_t size)
|
||||
{
|
||||
mps_word_t *p;
|
||||
|
||||
p = (mps_word_t *)addr;
|
||||
if(size == sizeof(mps_word_t)) /* single-word object? */
|
||||
p[0] = 1;
|
||||
else {
|
||||
p[0] = 2;
|
||||
p[1] = (mps_word_t)((char *)addr + size);
|
||||
}
|
||||
}
|
||||
|
||||
mps_bool_t dylan_ispad(mps_addr_t addr)
|
||||
{
|
||||
mps_word_t *p;
|
||||
|
||||
p = (mps_word_t *)addr;
|
||||
return p[0] == 1 || p[0] == 2;
|
||||
}
|
||||
|
||||
|
||||
/* The dylan format structures */
|
||||
|
||||
static struct mps_fmt_A_s dylan_fmt_A_s =
|
||||
{
|
||||
ALIGN,
|
||||
dylan_scan,
|
||||
dylan_skip,
|
||||
dylan_copy,
|
||||
dylan_fwd,
|
||||
dylan_isfwd,
|
||||
dylan_pad
|
||||
};
|
||||
|
||||
static struct mps_fmt_B_s dylan_fmt_B_s =
|
||||
{
|
||||
ALIGN,
|
||||
dylan_scan,
|
||||
dylan_skip,
|
||||
dylan_copy,
|
||||
dylan_fwd,
|
||||
dylan_isfwd,
|
||||
dylan_pad,
|
||||
dylan_class
|
||||
};
|
||||
|
||||
/* Functions returning the dylan format structures */
|
||||
|
||||
mps_fmt_A_s *dylan_fmt_A(void)
|
||||
{
|
||||
return &dylan_fmt_A_s;
|
||||
}
|
||||
|
||||
mps_fmt_B_s *dylan_fmt_B(void)
|
||||
{
|
||||
return &dylan_fmt_B_s;
|
||||
}
|
||||
|
||||
/* Format variety-independent version that picks the right format
|
||||
* variety and creates it. */
|
||||
|
||||
mps_res_t dylan_fmt(mps_fmt_t *mps_fmt_o, mps_arena_t arena)
|
||||
{
|
||||
return mps_fmt_create_B(mps_fmt_o, arena, dylan_fmt_B());
|
||||
}
|
||||
|
||||
/* The weak format structures */
|
||||
|
||||
static struct mps_fmt_A_s dylan_fmt_A_weak_s =
|
||||
{
|
||||
ALIGN,
|
||||
dylan_scan_weak,
|
||||
dylan_skip,
|
||||
no_copy,
|
||||
no_fwd,
|
||||
no_isfwd,
|
||||
no_pad
|
||||
};
|
||||
|
||||
static struct mps_fmt_B_s dylan_fmt_B_weak_s =
|
||||
{
|
||||
ALIGN,
|
||||
dylan_scan_weak,
|
||||
dylan_skip,
|
||||
no_copy,
|
||||
no_fwd,
|
||||
no_isfwd,
|
||||
no_pad,
|
||||
dylan_class
|
||||
};
|
||||
|
||||
/* Functions returning the weak format structures */
|
||||
|
||||
mps_fmt_A_s *dylan_fmt_A_weak(void)
|
||||
{
|
||||
return &dylan_fmt_A_weak_s;
|
||||
}
|
||||
|
||||
|
||||
mps_fmt_B_s *dylan_fmt_B_weak(void)
|
||||
{
|
||||
return &dylan_fmt_B_weak_s;
|
||||
}
|
||||
|
||||
|
||||
/* Format variety-independent version that picks the right format
|
||||
* variety and creates it. */
|
||||
|
||||
mps_res_t dylan_fmt_weak(mps_fmt_t *mps_fmt_o, mps_arena_t arena)
|
||||
{
|
||||
return mps_fmt_create_B(mps_fmt_o, arena, dylan_fmt_B_weak());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
75
mps/code/fmtdy.h
Normal file
75
mps/code/fmtdy.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/* fmtdy.h: DYLAN OBJECT FORMAT
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*/
|
||||
|
||||
#ifndef fmtdy_h
|
||||
#define fmtdy_h
|
||||
|
||||
#include "mps.h"
|
||||
|
||||
/* Low-level routines, exposed here so that the with-header format
|
||||
* can use common code. */
|
||||
extern mps_res_t dylan_scan1(mps_ss_t, mps_addr_t *);
|
||||
extern mps_res_t dylan_scan1_weak(mps_ss_t, mps_addr_t *);
|
||||
|
||||
/* Format */
|
||||
extern mps_fmt_A_s *dylan_fmt_A(void);
|
||||
extern mps_fmt_A_s *dylan_fmt_A_weak(void);
|
||||
extern mps_fmt_B_s *dylan_fmt_B(void);
|
||||
extern mps_fmt_B_s *dylan_fmt_B_weak(void);
|
||||
extern mps_res_t dylan_fmt(mps_fmt_t *, mps_arena_t);
|
||||
extern mps_res_t dylan_fmt_weak(mps_fmt_t *, mps_arena_t);
|
||||
|
||||
extern mps_addr_t dylan_weak_dependent(mps_addr_t);
|
||||
|
||||
extern mps_addr_t dylan_skip(mps_addr_t);
|
||||
extern void dylan_pad(mps_addr_t, size_t);
|
||||
extern mps_bool_t dylan_ispad(mps_addr_t);
|
||||
extern int dylan_wrapper_check(mps_word_t *);
|
||||
|
||||
/* Constants describing wrappers. Used only for debugging / testing */
|
||||
#define WW 0 /* offset of Wrapper-Wrapper */
|
||||
#define WC 1 /* offset of Class pointer*/
|
||||
#define WM 2 /* offset of subtype Mask */
|
||||
#define WF 3 /* offset of Fixed part descriptor */
|
||||
#define WV 4 /* offset of Vector part descriptor */
|
||||
#define WS 5 /* offset of Size field for pattern vector */
|
||||
#define WP 6 /* offset of Pattern 0, if present */
|
||||
|
||||
#define BASIC_WRAPPER_SIZE (WS + 1) /* size of wrapper with no patterns */
|
||||
|
||||
#define ALIGN sizeof(mps_word_t) /* alignment for Dylan format */
|
||||
|
||||
#endif /* fmtdy_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
248
mps/code/fmtdytst.c
Normal file
248
mps/code/fmtdytst.c
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
/* fmtdytst.c: DYLAN FORMAT TEST CODE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .readership: MPS developers, Dylan developers.
|
||||
*/
|
||||
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "mpslib.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define unused(param) ((void)param)
|
||||
|
||||
|
||||
static mps_word_t *ww = NULL;
|
||||
static mps_word_t *tvw;
|
||||
|
||||
|
||||
static mps_word_t dylan_make_WV(mps_word_t version, mps_word_t vb,
|
||||
mps_word_t es, mps_word_t vf)
|
||||
{
|
||||
assert((version & ((1 << 8) - 1)) == version);
|
||||
assert((vb & ((1 << 8) - 1)) == vb);
|
||||
assert((es & ((1 << 5) - 1)) == es);
|
||||
assert((vf & ((1 << 3) - 1)) == vf);
|
||||
|
||||
/* VERSION- ... VB------ reserved ES---VF- */
|
||||
return((version << (MPS_WORD_WIDTH - 8)) |
|
||||
(vb << 16) |
|
||||
(es << 3) |
|
||||
vf);
|
||||
}
|
||||
|
||||
mps_res_t dylan_make_wrappers(void)
|
||||
{
|
||||
if(ww == NULL) {
|
||||
ww = malloc(sizeof(mps_word_t) * (BASIC_WRAPPER_SIZE + 1));
|
||||
if(ww == NULL) return MPS_RES_MEMORY;
|
||||
tvw = malloc(sizeof(mps_word_t) * BASIC_WRAPPER_SIZE);
|
||||
if(tvw == NULL) {
|
||||
free(ww);
|
||||
return MPS_RES_MEMORY;
|
||||
}
|
||||
|
||||
/* Build a wrapper wrapper. */
|
||||
ww[WW] = (mps_word_t)ww;
|
||||
ww[WC] = (mps_word_t)ww; /* dummy class */
|
||||
ww[WM] = (1 << 2) | 1; /* dummy subtype_mask */
|
||||
ww[WF] = ((WS - 1) << 2) | 2;
|
||||
ww[WV] = dylan_make_WV(2, 0, 0, 0);
|
||||
ww[WS] = (1 << 2) | 1;
|
||||
ww[WP] = 1;
|
||||
|
||||
/* Build a wrapper for traceable vectors. */
|
||||
tvw[WW] = (mps_word_t)ww;
|
||||
tvw[WC] = (mps_word_t)ww; /* dummy class */
|
||||
tvw[WM] = (1 << 2) | 1; /* dummy subtype_mask */
|
||||
tvw[WF] = 0; /* no fixed part */
|
||||
tvw[WV] = dylan_make_WV(2, 0, 0, 2); /* traceable variable part */
|
||||
tvw[WS] = 1; /* no patterns */
|
||||
}
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
/* dylan_init -- turn raw memory into initialised dylan-vector (or pad)
|
||||
*
|
||||
* If the raw memory is large enough, initialises it to a dylan-vector,
|
||||
* whose slots are initialised to either dylan-ints, or valid refs, at
|
||||
* random.
|
||||
*
|
||||
* Caller must supply an array of valid refs to copy, via the "refs"
|
||||
* and "nr_refs" arguments. If "nr_refs" is 0, all slots are
|
||||
* initialized to dylan-ints: this may be useful for making leaf
|
||||
* objects.
|
||||
*
|
||||
* (Makes a pad if the raw memory is too small to hold a dylan-vector)
|
||||
*/
|
||||
|
||||
mps_res_t dylan_init(mps_addr_t addr, size_t size,
|
||||
mps_addr_t *refs, size_t nr_refs)
|
||||
{
|
||||
mps_res_t res;
|
||||
/* Make sure the size is aligned. */
|
||||
assert((size & (ALIGN-1)) == 0);
|
||||
|
||||
res = dylan_make_wrappers();
|
||||
if (res != MPS_RES_OK)
|
||||
return res;
|
||||
|
||||
/* If there is enough room, make a vector, otherwise just */
|
||||
/* make a padding object. */
|
||||
if(size >= sizeof(mps_word_t) * 2) {
|
||||
mps_word_t *p = (mps_word_t *)addr;
|
||||
mps_word_t i, t = (size / sizeof(mps_word_t)) - 2;
|
||||
|
||||
p[0] = (mps_word_t)tvw; /* install vector wrapper */
|
||||
p[1] = (t << 2) | 1; /* tag the vector length */
|
||||
for(i = 0; i < t; ++i) {
|
||||
mps_word_t r = rnd();
|
||||
|
||||
if(nr_refs == 0 || (r & 1))
|
||||
p[2+i] = ((r & ~(mps_word_t)3) | 1); /* random int */
|
||||
else
|
||||
p[2+i] = (mps_word_t)refs[(r >> 1) % nr_refs]; /* random ptr */
|
||||
}
|
||||
} else {
|
||||
dylan_pad(addr, size);
|
||||
}
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
mps_res_t make_dylan_vector(mps_word_t *v, mps_ap_t ap, size_t slots)
|
||||
{
|
||||
mps_res_t res;
|
||||
mps_addr_t addr;
|
||||
mps_word_t *p;
|
||||
size_t size;
|
||||
size_t i;
|
||||
|
||||
res = dylan_make_wrappers();
|
||||
if (res != MPS_RES_OK)
|
||||
return res;
|
||||
|
||||
size = (slots + 2) * sizeof(mps_word_t);
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, addr, ap, size);
|
||||
if (res != MPS_RES_OK)
|
||||
return res;
|
||||
|
||||
p = (mps_word_t *)addr;
|
||||
p[0] = (mps_word_t)tvw; /* install vector wrapper */
|
||||
p[1] = (slots << 2) | 1; /* tag the vector length */
|
||||
/* fill all slots with zero ints. */
|
||||
for (i=0; i<slots; ++i) {
|
||||
DYLAN_VECTOR_SLOT(p, i) = DYLAN_INT(0);
|
||||
}
|
||||
} while (!mps_commit(ap, addr, size));
|
||||
|
||||
*v = (mps_word_t)p;
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
void dylan_write(mps_addr_t addr, mps_addr_t *refs, size_t nr_refs)
|
||||
{
|
||||
mps_word_t *p = (mps_word_t *)addr;
|
||||
mps_word_t t = p[1] >> 2;
|
||||
|
||||
/* If the object is a vector, update a random entry. */
|
||||
if(p[0] == (mps_word_t)tvw && t > 0) {
|
||||
mps_word_t r = rnd();
|
||||
size_t i = 2 + (rnd() % t);
|
||||
|
||||
if(r & 1)
|
||||
p[i] = ((r & ~(mps_word_t)3) | 1); /* random int */
|
||||
else
|
||||
p[i] = (mps_word_t)refs[(r >> 1) % nr_refs]; /* random ptr */
|
||||
}
|
||||
}
|
||||
|
||||
/* Writes to a dylan object.
|
||||
Currently just swaps two refs if it can.
|
||||
This is only used in a certain way by certain tests, it doesn't have
|
||||
to be very general. */
|
||||
void dylan_mutate(mps_addr_t addr)
|
||||
{
|
||||
mps_word_t *p = (mps_word_t *)addr;
|
||||
|
||||
if(p[0] == (mps_word_t)tvw) {
|
||||
mps_word_t t = p[1] >> 2;
|
||||
|
||||
if(t > 0) {
|
||||
mps_word_t tmp;
|
||||
size_t i, j;
|
||||
|
||||
i = 2 + (rnd() % t);
|
||||
j = 2 + (rnd() % t);
|
||||
|
||||
tmp = p[i];
|
||||
p[i] = p[j];
|
||||
p[j] = tmp;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mps_addr_t dylan_read(mps_addr_t addr)
|
||||
{
|
||||
mps_word_t *p = (mps_word_t *)addr;
|
||||
|
||||
/* If the object is a vector, return a random entry. */
|
||||
if(p[0] == (mps_word_t)tvw) {
|
||||
mps_word_t t = p[1] >> 2;
|
||||
if(t > 0)
|
||||
return (mps_addr_t)p[2 + (rnd() % t)];
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
mps_bool_t dylan_check(mps_addr_t addr)
|
||||
{
|
||||
assert(addr != 0);
|
||||
assert(((mps_word_t)addr & (ALIGN-1)) == 0);
|
||||
assert(dylan_wrapper_check((mps_word_t *)((mps_word_t *)addr)[0]));
|
||||
/* .assert.unused: Asserts throw away their conditions */
|
||||
/* in hot varieties, so UNUSED is needed. */
|
||||
unused(addr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
70
mps/code/fmtdytst.h
Normal file
70
mps/code/fmtdytst.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* fmtdytst.h: DYLAN OBJECT FORMAT TESTING
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*/
|
||||
|
||||
#ifndef fmtdytst_h
|
||||
#define fmtdytst_h
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
|
||||
extern mps_res_t dylan_init(mps_addr_t addr, size_t size,
|
||||
mps_addr_t *refs, size_t nr_refs);
|
||||
extern void dylan_write(mps_addr_t addr,
|
||||
mps_addr_t *refs, size_t nr_refs);
|
||||
extern void dylan_mutate(mps_addr_t addr);
|
||||
extern mps_addr_t dylan_read(mps_addr_t addr);
|
||||
extern mps_bool_t dylan_check(mps_addr_t addr);
|
||||
extern void dylan_pad(mps_addr_t addr, size_t size);
|
||||
extern int dylan_wrapper_check(mps_word_t *w);
|
||||
extern mps_res_t dylan_make_wrappers(void);
|
||||
|
||||
extern mps_res_t make_dylan_vector(mps_word_t *v, mps_ap_t ap, size_t slots);
|
||||
|
||||
#define DYLAN_VECTOR_SLOT(o,n) (((mps_word_t *) (o))[(n)+2])
|
||||
|
||||
#define DYLAN_INT(n) (((mps_word_t)(n) << 2) | 1)
|
||||
|
||||
#define INT_DYI(n) ( (n) <= DYLAN_UINT_MAX ? DYLAN_INT(n) : (mps_word_t)fail() )
|
||||
|
||||
|
||||
#define DYLAN_INT_INT(d) ((mps_word_t)(d) >> 2)
|
||||
|
||||
#define DYI_INT(d) ( ((d) & 0x3) == 0x1 ? DYLAN_INT_INT(d) : (mps_word_t)fail() )
|
||||
|
||||
#define DYLAN_UINT_MAX ((mps_word_t)-1 >> 2)
|
||||
#define DYLAN_UINT_MASK DYLAN_UINT_MAX
|
||||
|
||||
#endif /* fmtdy_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
229
mps/code/fmthe.c
Normal file
229
mps/code/fmthe.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/* fmthe.c: DYLAN-LIKE OBJECT FORMAT WITH HEADERS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (c) 2002 Global Graphics Software.
|
||||
*
|
||||
* Uses <code/fmtdy.c> for the actual Dylan format, and just adds
|
||||
* a thin layer to handle the object headers themselves.
|
||||
*/
|
||||
|
||||
#include "fmtdy.h"
|
||||
#include "fmtno.h"
|
||||
#include "fmthe.h"
|
||||
#include "mps.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
|
||||
#define assert(cond) Insist(cond)
|
||||
#define notreached() assert(0)
|
||||
|
||||
#define AddHeader(p) ((mps_addr_t)((char*)(p) + headerSIZE))
|
||||
|
||||
static mps_fmt_A_s *dylan_format;
|
||||
|
||||
static mps_res_t dylan_header_scan(mps_ss_t mps_ss,
|
||||
mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
mps_res_t res;
|
||||
mps_addr_t p = base;
|
||||
|
||||
while(p < limit) {
|
||||
mps_word_t header = (mps_word_t)*(int*)((char*)p - headerSIZE);
|
||||
switch(headerType(header)) {
|
||||
case realTYPE:
|
||||
assert(header == realHeader);
|
||||
break;
|
||||
case padTYPE:
|
||||
p = (mps_addr_t)((char*)p + headerPadSize(header));
|
||||
continue;
|
||||
default:
|
||||
notreached();
|
||||
break;
|
||||
}
|
||||
res = dylan_scan1(mps_ss, &p);
|
||||
if(res) return res;
|
||||
p = AddHeader(p);
|
||||
}
|
||||
|
||||
assert(p <= AddHeader(limit));
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
static mps_res_t dylan_header_scan_weak(mps_ss_t mps_ss,
|
||||
mps_addr_t base,
|
||||
mps_addr_t limit)
|
||||
{
|
||||
mps_res_t res;
|
||||
|
||||
while(base < limit) {
|
||||
mps_word_t header;
|
||||
header = (mps_word_t)*(int*)((char*)base - headerSIZE);
|
||||
switch(headerType(header)) {
|
||||
case realTYPE:
|
||||
assert(header == realHeader);
|
||||
break;
|
||||
case padTYPE:
|
||||
base = (mps_addr_t)((char*)base + headerPadSize(header));
|
||||
continue;
|
||||
default:
|
||||
notreached();
|
||||
break;
|
||||
}
|
||||
|
||||
res = dylan_scan1_weak(mps_ss, &base);
|
||||
if(res) return res;
|
||||
base = AddHeader(base);
|
||||
}
|
||||
|
||||
assert(base <= AddHeader(limit));
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
static mps_addr_t dylan_header_skip(mps_addr_t object)
|
||||
{
|
||||
mps_addr_t *p; /* cursor in object */
|
||||
mps_word_t header;
|
||||
header = (mps_word_t)*(int*)((char*)object - headerSIZE);
|
||||
switch(headerType(header)) {
|
||||
case realTYPE:
|
||||
assert(header == realHeader);
|
||||
break;
|
||||
case padTYPE:
|
||||
return (mps_addr_t)((char*)object + headerPadSize(header));
|
||||
default:
|
||||
notreached();
|
||||
break;
|
||||
}
|
||||
|
||||
p = dylan_format->skip(object);
|
||||
p = AddHeader(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static mps_addr_t dylan_header_isfwd(mps_addr_t object)
|
||||
{
|
||||
mps_word_t header;
|
||||
|
||||
header = (mps_word_t)*(int*)((char*)object - headerSIZE);
|
||||
if (headerType(header) != realTYPE)
|
||||
return NULL;
|
||||
|
||||
assert(header == realHeader);
|
||||
|
||||
return dylan_format->isfwd(object);
|
||||
}
|
||||
|
||||
|
||||
static void dylan_header_pad(mps_addr_t addr, size_t fullSize)
|
||||
{
|
||||
*(int*)addr = (int)padHeader(fullSize);
|
||||
}
|
||||
|
||||
|
||||
/* HeaderFormat -- format descriptor for this format */
|
||||
|
||||
static struct mps_fmt_auto_header_s HeaderFormat =
|
||||
{
|
||||
ALIGN,
|
||||
dylan_header_scan,
|
||||
dylan_header_skip,
|
||||
NULL, /* later overwritten by dylan format forward method */
|
||||
dylan_header_isfwd,
|
||||
dylan_header_pad,
|
||||
(size_t)headerSIZE
|
||||
};
|
||||
|
||||
|
||||
/* HeaderWeakFormat -- format descriptor for this format */
|
||||
|
||||
static struct mps_fmt_auto_header_s HeaderWeakFormat =
|
||||
{
|
||||
ALIGN,
|
||||
dylan_header_scan_weak,
|
||||
dylan_header_skip,
|
||||
no_fwd,
|
||||
no_isfwd,
|
||||
no_pad,
|
||||
(size_t)headerSIZE
|
||||
};
|
||||
|
||||
|
||||
/* EnsureHeaderFormat -- create a format object for this format */
|
||||
|
||||
mps_res_t EnsureHeaderFormat(mps_fmt_t *mps_fmt_o, mps_arena_t arena)
|
||||
{
|
||||
dylan_format = dylan_fmt_A();
|
||||
HeaderFormat.fwd = dylan_format->fwd;
|
||||
return mps_fmt_create_auto_header(mps_fmt_o, arena, &HeaderFormat);
|
||||
}
|
||||
|
||||
|
||||
/* EnsureHeaderWeakFormat -- create a format object for the weak format */
|
||||
|
||||
mps_res_t EnsureHeaderWeakFormat(mps_fmt_t *mps_fmt_o, mps_arena_t arena)
|
||||
{
|
||||
dylan_format = dylan_fmt_A();
|
||||
return mps_fmt_create_auto_header(mps_fmt_o, arena, &HeaderWeakFormat);
|
||||
}
|
||||
|
||||
|
||||
/* HeaderFormatCheck -- check an object in this format */
|
||||
|
||||
mps_res_t HeaderFormatCheck(mps_addr_t addr)
|
||||
{
|
||||
if (addr != 0 && ((mps_word_t)addr & (ALIGN-1)) == 0
|
||||
&& dylan_wrapper_check((mps_word_t *)((mps_word_t *)addr)[0]))
|
||||
return MPS_RES_OK;
|
||||
else
|
||||
return MPS_RES_FAIL;
|
||||
}
|
||||
|
||||
/* HeaderWeakFormatCheck -- check an object in this format */
|
||||
|
||||
mps_res_t HeaderWeakFormatCheck(mps_addr_t addr)
|
||||
{
|
||||
if (addr != 0 && ((mps_word_t)addr & (ALIGN-1)) == 0
|
||||
&& dylan_wrapper_check((mps_word_t *)((mps_word_t *)addr)[0]))
|
||||
return MPS_RES_OK;
|
||||
else
|
||||
return MPS_RES_FAIL;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
58
mps/code/fmthe.h
Normal file
58
mps/code/fmthe.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* fmthe.h: HEADERS FOR DYLAN-LIKE OBJECT FORMATS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*/
|
||||
|
||||
#ifndef fmthe_h
|
||||
#define fmthe_h
|
||||
|
||||
#include "mps.h"
|
||||
|
||||
/* Formats */
|
||||
extern mps_res_t EnsureHeaderFormat(mps_fmt_t *, mps_arena_t);
|
||||
extern mps_res_t EnsureHeaderWeakFormat(mps_fmt_t *, mps_arena_t);
|
||||
extern mps_res_t HeaderFormatCheck(mps_addr_t addr);
|
||||
extern mps_res_t HeaderWeakFormatCheck(mps_addr_t addr);
|
||||
|
||||
#define headerSIZE (32)
|
||||
#define headerTypeBits 8
|
||||
#define realTYPE 0x33
|
||||
#define realHeader (realTYPE + 0x12345600)
|
||||
#define padTYPE 0xaa
|
||||
#define headerType(header) ((header) & (((mps_word_t)1 << headerTypeBits) - 1))
|
||||
#define headerPadSize(header) ((header) >> headerTypeBits)
|
||||
#define padHeader(size) ((size << headerTypeBits) | padTYPE)
|
||||
|
||||
#endif /* fmthe_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
149
mps/code/fmtno.c
Normal file
149
mps/code/fmtno.c
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/* fmtno.c: NULL OBJECT FORMAT IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .readership: MPS developers
|
||||
*/
|
||||
|
||||
|
||||
#include "fmtno.h"
|
||||
#include "mps.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define notreached() assert(0)
|
||||
#define unused(param) ((void)param)
|
||||
|
||||
|
||||
#define ALIGN sizeof(mps_word_t)
|
||||
|
||||
/* Functions for the null format. */
|
||||
|
||||
mps_res_t no_scan(mps_ss_t mps_ss,
|
||||
mps_addr_t base,
|
||||
mps_addr_t limit)
|
||||
{
|
||||
unused(mps_ss); unused(base); unused(limit);
|
||||
notreached();
|
||||
return 0;
|
||||
}
|
||||
|
||||
mps_addr_t no_skip(mps_addr_t object)
|
||||
{
|
||||
unused(object);
|
||||
notreached();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void no_copy(mps_addr_t old,
|
||||
mps_addr_t new)
|
||||
{
|
||||
unused(old); unused(new);
|
||||
notreached();
|
||||
}
|
||||
|
||||
void no_fwd(mps_addr_t old,
|
||||
mps_addr_t new)
|
||||
{
|
||||
unused(old); unused(new);
|
||||
notreached();
|
||||
}
|
||||
|
||||
mps_addr_t no_isfwd(mps_addr_t object)
|
||||
{
|
||||
unused(object);
|
||||
notreached();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void no_pad(mps_addr_t addr,
|
||||
size_t size)
|
||||
{
|
||||
unused(addr); unused(size);
|
||||
notreached();
|
||||
}
|
||||
|
||||
mps_addr_t no_class(mps_addr_t obj)
|
||||
{
|
||||
unused(obj);
|
||||
notreached();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The null format structures */
|
||||
|
||||
static struct mps_fmt_A_s no_fmt_A_s =
|
||||
{
|
||||
ALIGN,
|
||||
no_scan,
|
||||
no_skip,
|
||||
no_copy,
|
||||
no_fwd,
|
||||
no_isfwd,
|
||||
no_pad
|
||||
};
|
||||
|
||||
static struct mps_fmt_B_s no_fmt_B_s =
|
||||
{
|
||||
ALIGN,
|
||||
no_scan,
|
||||
no_skip,
|
||||
no_copy,
|
||||
no_fwd,
|
||||
no_isfwd,
|
||||
no_pad,
|
||||
no_class
|
||||
};
|
||||
|
||||
/* Functions returning the null format structures. */
|
||||
|
||||
mps_fmt_A_s *no_fmt_A(void)
|
||||
{
|
||||
return &no_fmt_A_s;
|
||||
}
|
||||
|
||||
mps_fmt_B_s *no_fmt_B(void)
|
||||
{
|
||||
return &no_fmt_B_s;
|
||||
}
|
||||
|
||||
/* Format variety-independent version that picks the right format
|
||||
* variety and creates it. */
|
||||
|
||||
mps_res_t no_fmt(mps_fmt_t *mps_fmt_o, mps_arena_t arena)
|
||||
{
|
||||
return mps_fmt_create_B(mps_fmt_o, arena, no_fmt_B());
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
54
mps/code/fmtno.h
Normal file
54
mps/code/fmtno.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* fmtdy.h: NULL OBJECT FORMAT
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*/
|
||||
|
||||
#ifndef fmtno_h
|
||||
#define fmtno_h
|
||||
|
||||
#include "mps.h"
|
||||
|
||||
extern mps_res_t no_scan(mps_ss_t, mps_addr_t, mps_addr_t);
|
||||
extern mps_addr_t no_skip(mps_addr_t);
|
||||
extern void no_copy(mps_addr_t, mps_addr_t);
|
||||
extern void no_fwd(mps_addr_t, mps_addr_t);
|
||||
extern mps_addr_t no_isfwd(mps_addr_t);
|
||||
extern void no_pad(mps_addr_t, size_t);
|
||||
extern mps_addr_t no_class(mps_addr_t);
|
||||
|
||||
extern mps_fmt_A_s *no_fmt_A(void);
|
||||
extern mps_fmt_B_s *no_fmt_B(void);
|
||||
extern mps_res_t no_fmt(mps_fmt_t *, mps_arena_t);
|
||||
|
||||
#endif /* fmtno_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
488
mps/code/fmtscheme.c
Normal file
488
mps/code/fmtscheme.c
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
/* fmtscheme.c: SCHEME OBJECT FORMAT IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "fmtscheme.h"
|
||||
#include "testlib.h"
|
||||
|
||||
|
||||
/* special objects */
|
||||
|
||||
static obj_t obj_true; /* #t, boolean true */
|
||||
static obj_t obj_false; /* #f, boolean false */
|
||||
|
||||
|
||||
/* MPS globals */
|
||||
|
||||
mps_arena_t scheme_arena; /* the arena */
|
||||
mps_pool_t obj_pool; /* pool for ordinary Scheme objects */
|
||||
mps_ap_t obj_ap; /* allocation point used to allocate objects */
|
||||
|
||||
|
||||
/* make_* -- object constructors */
|
||||
|
||||
#define ALIGNMENT sizeof(mps_word_t)
|
||||
|
||||
/* Align size upwards to the next multiple of the word size. */
|
||||
#define ALIGN_WORD(size) \
|
||||
(((size) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
|
||||
|
||||
/* Align size upwards to the next multiple of the word size, and
|
||||
* additionally ensure that it's big enough to store a forwarding
|
||||
* pointer. Evaluates its argument twice. */
|
||||
#define ALIGN_OBJ(size) \
|
||||
(ALIGN_WORD(size) >= ALIGN_WORD(sizeof(fwd_s)) \
|
||||
? ALIGN_WORD(size) \
|
||||
: ALIGN_WORD(sizeof(fwd_s)))
|
||||
|
||||
obj_t scheme_make_bool(int condition)
|
||||
{
|
||||
return condition ? obj_true : obj_false;
|
||||
}
|
||||
|
||||
obj_t scheme_make_pair(mps_ap_t ap, obj_t car, obj_t cdr)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(sizeof(pair_s));
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_pair");
|
||||
obj = addr;
|
||||
obj->pair.type = TYPE_PAIR;
|
||||
CAR(obj) = car;
|
||||
CDR(obj) = cdr;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_integer(mps_ap_t ap, long integer)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(sizeof(integer_s));
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_integer");
|
||||
obj = addr;
|
||||
obj->integer.type = TYPE_INTEGER;
|
||||
obj->integer.integer = integer;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_symbol(mps_ap_t ap, size_t length, char string[])
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(offsetof(symbol_s, string) + length+1);
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_symbol");
|
||||
obj = addr;
|
||||
obj->symbol.type = TYPE_SYMBOL;
|
||||
obj->symbol.length = length;
|
||||
memcpy(obj->symbol.string, string, length+1);
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_string(mps_ap_t ap, size_t length, char string[])
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(offsetof(string_s, string) + length+1);
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_string");
|
||||
obj = addr;
|
||||
obj->string.type = TYPE_STRING;
|
||||
obj->string.length = length;
|
||||
if (string) memcpy(obj->string.string, string, length+1);
|
||||
else memset(obj->string.string, 0, length+1);
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_special(mps_ap_t ap, char *string)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(sizeof(special_s));
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_special");
|
||||
obj = addr;
|
||||
obj->special.type = TYPE_SPECIAL;
|
||||
obj->special.name = string;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_operator(mps_ap_t ap, char *name,
|
||||
entry_t entry, obj_t arguments,
|
||||
obj_t body, obj_t env, obj_t op_env)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(sizeof(operator_s));
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_operator");
|
||||
obj = addr;
|
||||
obj->operator.type = TYPE_OPERATOR;
|
||||
obj->operator.name = name;
|
||||
obj->operator.entry = entry;
|
||||
obj->operator.arguments = arguments;
|
||||
obj->operator.body = body;
|
||||
obj->operator.env = env;
|
||||
obj->operator.op_env = op_env;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_port(mps_ap_t ap, obj_t name, FILE *stream)
|
||||
{
|
||||
mps_addr_t port_ref;
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(sizeof(port_s));
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_port");
|
||||
obj = addr;
|
||||
obj->port.type = TYPE_PORT;
|
||||
obj->port.name = name;
|
||||
obj->port.stream = stream;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
port_ref = obj;
|
||||
mps_finalize(scheme_arena, &port_ref);
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_character(mps_ap_t ap, char c)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(sizeof(character_s));
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_character");
|
||||
obj = addr;
|
||||
obj->character.type = TYPE_CHARACTER;
|
||||
obj->character.c = c;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_vector(mps_ap_t ap, size_t length, obj_t fill)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(offsetof(vector_s, vector) + length * sizeof(obj_t));
|
||||
do {
|
||||
size_t i;
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_vector");
|
||||
obj = addr;
|
||||
obj->vector.type = TYPE_VECTOR;
|
||||
obj->vector.length = length;
|
||||
for(i = 0; i < length; ++i)
|
||||
obj->vector.vector[i] = fill;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_buckets(mps_ap_t ap, size_t length)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t size = ALIGN_OBJ(offsetof(buckets_s, bucket) + length * sizeof(obj->buckets.bucket[0]));
|
||||
do {
|
||||
size_t i;
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_buckets");
|
||||
obj = addr;
|
||||
obj->buckets.type = TYPE_BUCKETS;
|
||||
obj->buckets.length = length;
|
||||
obj->buckets.used = 0;
|
||||
obj->buckets.deleted = 0;
|
||||
for(i = 0; i < length; ++i) {
|
||||
obj->buckets.bucket[i].key = NULL;
|
||||
obj->buckets.bucket[i].value = NULL;
|
||||
}
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj_t scheme_make_table(mps_ap_t ap, size_t length, hash_t hashf, cmp_t cmpf)
|
||||
{
|
||||
obj_t obj;
|
||||
mps_addr_t addr;
|
||||
size_t l, size = ALIGN_OBJ(sizeof(table_s));
|
||||
do {
|
||||
mps_res_t res = mps_reserve(&addr, ap, size);
|
||||
if (res != MPS_RES_OK) error("out of memory in make_table");
|
||||
obj = addr;
|
||||
obj->table.type = TYPE_TABLE;
|
||||
obj->table.buckets = NULL;
|
||||
} while(!mps_commit(ap, addr, size));
|
||||
obj->table.hash = hashf;
|
||||
obj->table.cmp = cmpf;
|
||||
/* round up to next power of 2 */
|
||||
for(l = 1; l < length; l *= 2);
|
||||
obj->table.buckets = scheme_make_buckets(ap, l);
|
||||
mps_ld_reset(&obj->table.ld, scheme_arena);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/* MPS Format */
|
||||
|
||||
static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
#define FIX(ref) \
|
||||
do { \
|
||||
mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \
|
||||
mps_res_t res = MPS_FIX12(ss, &_addr); \
|
||||
if (res != MPS_RES_OK) return res; \
|
||||
(ref) = _addr; \
|
||||
} while(0)
|
||||
|
||||
MPS_SCAN_BEGIN(ss) {
|
||||
while (base < limit) {
|
||||
obj_t obj = base;
|
||||
switch (TYPE(obj)) {
|
||||
case TYPE_PAIR:
|
||||
case TYPE_PROMISE:
|
||||
FIX(CAR(obj));
|
||||
FIX(CDR(obj));
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(pair_s));
|
||||
break;
|
||||
case TYPE_INTEGER:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(integer_s));
|
||||
break;
|
||||
case TYPE_SYMBOL:
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1);
|
||||
break;
|
||||
case TYPE_SPECIAL:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(special_s));
|
||||
break;
|
||||
case TYPE_OPERATOR:
|
||||
FIX(obj->operator.arguments);
|
||||
FIX(obj->operator.body);
|
||||
FIX(obj->operator.env);
|
||||
FIX(obj->operator.op_env);
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(operator_s));
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1);
|
||||
break;
|
||||
case TYPE_PORT:
|
||||
FIX(obj->port.name);
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(port_s));
|
||||
break;
|
||||
case TYPE_CHARACTER:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(character_s));
|
||||
break;
|
||||
case TYPE_VECTOR:
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < obj->vector.length; ++i)
|
||||
FIX(obj->vector.vector[i]);
|
||||
}
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(vector_s, vector) +
|
||||
obj->vector.length * sizeof(obj->vector.vector[0]));
|
||||
break;
|
||||
case TYPE_BUCKETS:
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < obj->buckets.length; ++i) {
|
||||
FIX(obj->buckets.bucket[i].key);
|
||||
FIX(obj->buckets.bucket[i].value);
|
||||
}
|
||||
}
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(buckets_s, bucket) +
|
||||
obj->buckets.length * sizeof(obj->buckets.bucket[0]));
|
||||
break;
|
||||
case TYPE_TABLE:
|
||||
FIX(obj->table.buckets);
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(table_s));
|
||||
break;
|
||||
case TYPE_FWD2:
|
||||
base = (char *)base + ALIGN_WORD(sizeof(fwd2_s));
|
||||
break;
|
||||
case TYPE_FWD:
|
||||
base = (char *)base + ALIGN_WORD(obj->fwd.size);
|
||||
break;
|
||||
case TYPE_PAD1:
|
||||
base = (char *)base + ALIGN_WORD(sizeof(pad1_s));
|
||||
break;
|
||||
case TYPE_PAD:
|
||||
base = (char *)base + ALIGN_WORD(obj->pad.size);
|
||||
break;
|
||||
default:
|
||||
error("Unexpected object on the heap\n");
|
||||
return MPS_RES_FAIL;
|
||||
}
|
||||
}
|
||||
} MPS_SCAN_END(ss);
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
static mps_addr_t obj_skip(mps_addr_t base)
|
||||
{
|
||||
obj_t obj = base;
|
||||
switch (TYPE(obj)) {
|
||||
case TYPE_PAIR:
|
||||
case TYPE_PROMISE:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(pair_s));
|
||||
break;
|
||||
case TYPE_INTEGER:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(integer_s));
|
||||
break;
|
||||
case TYPE_SYMBOL:
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(symbol_s, string) + obj->symbol.length + 1);
|
||||
break;
|
||||
case TYPE_SPECIAL:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(special_s));
|
||||
break;
|
||||
case TYPE_OPERATOR:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(operator_s));
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(string_s, string) + obj->string.length + 1);
|
||||
break;
|
||||
case TYPE_PORT:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(port_s));
|
||||
break;
|
||||
case TYPE_CHARACTER:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(character_s));
|
||||
break;
|
||||
case TYPE_VECTOR:
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(vector_s, vector) +
|
||||
obj->vector.length * sizeof(obj->vector.vector[0]));
|
||||
break;
|
||||
case TYPE_BUCKETS:
|
||||
base = (char *)base +
|
||||
ALIGN_OBJ(offsetof(buckets_s, bucket) +
|
||||
obj->buckets.length * sizeof(obj->buckets.bucket[0]));
|
||||
break;
|
||||
case TYPE_TABLE:
|
||||
base = (char *)base + ALIGN_OBJ(sizeof(table_s));
|
||||
break;
|
||||
case TYPE_FWD2:
|
||||
base = (char *)base + ALIGN_WORD(sizeof(fwd2_s));
|
||||
break;
|
||||
case TYPE_FWD:
|
||||
base = (char *)base + ALIGN_WORD(obj->fwd.size);
|
||||
break;
|
||||
case TYPE_PAD:
|
||||
base = (char *)base + ALIGN_WORD(obj->pad.size);
|
||||
break;
|
||||
case TYPE_PAD1:
|
||||
base = (char *)base + ALIGN_WORD(sizeof(pad1_s));
|
||||
break;
|
||||
default:
|
||||
error("Unexpected object on the heap\n");
|
||||
return NULL;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
static mps_addr_t obj_isfwd(mps_addr_t addr)
|
||||
{
|
||||
obj_t obj = addr;
|
||||
switch (TYPE(obj)) {
|
||||
case TYPE_FWD2:
|
||||
return obj->fwd2.fwd;
|
||||
case TYPE_FWD:
|
||||
return obj->fwd.fwd;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void obj_fwd(mps_addr_t old, mps_addr_t new)
|
||||
{
|
||||
obj_t obj = old;
|
||||
mps_addr_t limit = obj_skip(old);
|
||||
size_t size = (size_t)((char *)limit - (char *)old);
|
||||
cdie(size >= ALIGN_WORD(sizeof(fwd2_s)), "bad size in obj_fwd");
|
||||
if (size == ALIGN_WORD(sizeof(fwd2_s))) {
|
||||
TYPE(obj) = TYPE_FWD2;
|
||||
obj->fwd2.fwd = new;
|
||||
} else {
|
||||
TYPE(obj) = TYPE_FWD;
|
||||
obj->fwd.fwd = new;
|
||||
obj->fwd.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
static void obj_pad(mps_addr_t addr, size_t size)
|
||||
{
|
||||
obj_t obj = addr;
|
||||
cdie(size >= ALIGN_WORD(sizeof(pad1_s)), "bad size in obj_pad");
|
||||
if (size == ALIGN_WORD(sizeof(pad1_s))) {
|
||||
TYPE(obj) = TYPE_PAD1;
|
||||
} else {
|
||||
TYPE(obj) = TYPE_PAD;
|
||||
obj->pad.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
void scheme_fmt(mps_fmt_t *fmt)
|
||||
{
|
||||
mps_res_t res;
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, ALIGNMENT);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, obj_scan);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, obj_skip);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, obj_fwd);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, obj_isfwd);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, obj_pad);
|
||||
res = mps_fmt_create_k(fmt, scheme_arena, args);
|
||||
} MPS_ARGS_END(args);
|
||||
if (res != MPS_RES_OK) error("Couldn't create obj format");
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
221
mps/code/fmtscheme.h
Normal file
221
mps/code/fmtscheme.h
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/* fmtscheme.h: SCHEME OBJECT FORMAT INTERFACE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*/
|
||||
|
||||
#ifndef fmtscheme_h
|
||||
#define fmtscheme_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "mps.h"
|
||||
|
||||
typedef union obj_u *obj_t;
|
||||
typedef obj_t (*entry_t)(obj_t env, obj_t op_env, obj_t operator, obj_t rands);
|
||||
typedef unsigned long (*hash_t)(obj_t obj, mps_ld_t ld);
|
||||
typedef int (*cmp_t)(obj_t obj1, obj_t obj2);
|
||||
|
||||
typedef int type_t;
|
||||
enum {
|
||||
TYPE_PAIR,
|
||||
TYPE_INTEGER,
|
||||
TYPE_SYMBOL,
|
||||
TYPE_SPECIAL,
|
||||
TYPE_OPERATOR,
|
||||
TYPE_STRING,
|
||||
TYPE_PORT,
|
||||
TYPE_PROMISE,
|
||||
TYPE_CHARACTER,
|
||||
TYPE_VECTOR,
|
||||
TYPE_TABLE,
|
||||
TYPE_BUCKETS,
|
||||
TYPE_FWD2, /* two-word forwarding object */
|
||||
TYPE_FWD, /* three words and up forwarding object */
|
||||
TYPE_PAD1, /* one-word padding object */
|
||||
TYPE_PAD /* two words and up padding object */
|
||||
};
|
||||
|
||||
typedef struct type_s {
|
||||
type_t type;
|
||||
} type_s;
|
||||
|
||||
typedef struct pair_s {
|
||||
type_t type; /* TYPE_PAIR */
|
||||
obj_t car, cdr; /* first and second projections */
|
||||
} pair_s;
|
||||
|
||||
typedef struct symbol_s {
|
||||
type_t type; /* TYPE_SYMBOL */
|
||||
size_t length; /* length of symbol string (excl. NUL) */
|
||||
char string[1]; /* symbol string, NUL terminated */
|
||||
} symbol_s;
|
||||
|
||||
typedef struct integer_s {
|
||||
type_t type; /* TYPE_INTEGER */
|
||||
long integer; /* the integer */
|
||||
} integer_s;
|
||||
|
||||
typedef struct special_s {
|
||||
type_t type; /* TYPE_SPECIAL */
|
||||
char *name; /* printed representation, NUL terminated */
|
||||
} special_s;
|
||||
|
||||
typedef struct operator_s {
|
||||
type_t type; /* TYPE_OPERATOR */
|
||||
char *name; /* printed name, NUL terminated */
|
||||
entry_t entry; /* entry point -- see eval() */
|
||||
obj_t arguments, body; /* function arguments and code */
|
||||
obj_t env, op_env; /* closure environments */
|
||||
} operator_s;
|
||||
|
||||
typedef struct string_s {
|
||||
type_t type; /* TYPE_STRING */
|
||||
size_t length; /* number of chars in string */
|
||||
char string[1]; /* string, NUL terminated */
|
||||
} string_s;
|
||||
|
||||
typedef struct port_s {
|
||||
type_t type; /* TYPE_PORT */
|
||||
obj_t name; /* name of stream */
|
||||
FILE *stream;
|
||||
} port_s;
|
||||
|
||||
typedef struct character_s {
|
||||
type_t type; /* TYPE_CHARACTER */
|
||||
char c; /* the character */
|
||||
} character_s;
|
||||
|
||||
typedef struct vector_s {
|
||||
type_t type; /* TYPE_VECTOR */
|
||||
size_t length; /* number of elements */
|
||||
obj_t vector[1]; /* vector elements */
|
||||
} vector_s;
|
||||
|
||||
typedef struct table_s {
|
||||
type_t type; /* TYPE_TABLE */
|
||||
hash_t hash; /* hash function */
|
||||
cmp_t cmp; /* comparison function */
|
||||
mps_ld_s ld; /* location dependency */
|
||||
obj_t buckets; /* hash buckets */
|
||||
} table_s;
|
||||
|
||||
typedef struct buckets_s {
|
||||
type_t type; /* TYPE_BUCKETS */
|
||||
size_t length; /* number of buckets */
|
||||
size_t used; /* number of buckets in use */
|
||||
size_t deleted; /* number of deleted buckets */
|
||||
struct bucket_s {
|
||||
obj_t key, value;
|
||||
} bucket[1]; /* hash buckets */
|
||||
} buckets_s;
|
||||
|
||||
|
||||
/* fwd2, fwd, pad1, pad -- MPS forwarding and padding objects */
|
||||
|
||||
typedef struct fwd2_s {
|
||||
type_t type; /* TYPE_FWD2 */
|
||||
obj_t fwd; /* forwarded object */
|
||||
} fwd2_s;
|
||||
|
||||
typedef struct fwd_s {
|
||||
type_t type; /* TYPE_FWD */
|
||||
obj_t fwd; /* forwarded object */
|
||||
size_t size; /* total size of this object */
|
||||
} fwd_s;
|
||||
|
||||
typedef struct pad1_s {
|
||||
type_t type; /* TYPE_PAD1 */
|
||||
} pad1_s;
|
||||
|
||||
typedef struct pad_s {
|
||||
type_t type; /* TYPE_PAD */
|
||||
size_t size; /* total size of this object */
|
||||
} pad_s;
|
||||
|
||||
|
||||
typedef union obj_u {
|
||||
type_s type; /* one of TYPE_* */
|
||||
pair_s pair;
|
||||
symbol_s symbol;
|
||||
integer_s integer;
|
||||
special_s special;
|
||||
operator_s operator;
|
||||
string_s string;
|
||||
port_s port;
|
||||
character_s character;
|
||||
vector_s vector;
|
||||
table_s table;
|
||||
buckets_s buckets;
|
||||
fwd2_s fwd2;
|
||||
fwd_s fwd;
|
||||
pad_s pad;
|
||||
} obj_s;
|
||||
|
||||
|
||||
/* structure macros */
|
||||
|
||||
#define TYPE(obj) ((obj)->type.type)
|
||||
#define CAR(obj) ((obj)->pair.car)
|
||||
#define CDR(obj) ((obj)->pair.cdr)
|
||||
#define CAAR(obj) CAR(CAR(obj))
|
||||
#define CADR(obj) CAR(CDR(obj))
|
||||
#define CDAR(obj) CDR(CAR(obj))
|
||||
#define CDDR(obj) CDR(CDR(obj))
|
||||
#define CADDR(obj) CAR(CDDR(obj))
|
||||
#define CDDDR(obj) CDR(CDDR(obj))
|
||||
#define CDDAR(obj) CDR(CDAR(obj))
|
||||
#define CADAR(obj) CAR(CDAR(obj))
|
||||
|
||||
|
||||
extern obj_t scheme_make_bool(int condition);
|
||||
extern obj_t scheme_make_pair(mps_ap_t ap, obj_t car, obj_t cdr);
|
||||
extern obj_t scheme_make_integer(mps_ap_t ap, long integer);
|
||||
extern obj_t scheme_make_symbol(mps_ap_t ap, size_t length, char string[]);
|
||||
extern obj_t scheme_make_string(mps_ap_t ap, size_t length, char string[]);
|
||||
extern obj_t scheme_make_special(mps_ap_t ap, char *string);
|
||||
extern obj_t scheme_make_operator(mps_ap_t ap, char *name, entry_t entry,
|
||||
obj_t arguments, obj_t body, obj_t env,
|
||||
obj_t op_env);
|
||||
extern obj_t scheme_make_port(mps_ap_t ap, obj_t name, FILE *stream);
|
||||
extern obj_t scheme_make_character(mps_ap_t ap, char c);
|
||||
extern obj_t scheme_make_vector(mps_ap_t ap, size_t length, obj_t fill);
|
||||
extern obj_t scheme_make_buckets(mps_ap_t ap, size_t length);
|
||||
extern obj_t scheme_make_table(mps_ap_t ap, size_t length, hash_t hashf,
|
||||
cmp_t cmpf);
|
||||
extern void scheme_fmt(mps_fmt_t *fmt);
|
||||
|
||||
extern mps_arena_t scheme_arena;
|
||||
extern mps_pool_t obj_pool;
|
||||
extern mps_ap_t obj_ap;
|
||||
|
||||
#endif /* fmtscheme_h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
219
mps/code/forktest.c
Normal file
219
mps/code/forktest.c
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
/* forktest.c: FORK SAFETY TEST
|
||||
*
|
||||
* $Id: //info.ravenbrook.com/project/mps/branch/2018-06-13/fork/code/tagtest.c#1 $
|
||||
* Copyright (c) 2018-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview: This test case is a regression test for job004062. It
|
||||
* checks that the MPS correctly runs in the child process after a
|
||||
* fork() on FreeBSD, Linux or macOS.
|
||||
*
|
||||
* .format: This test case uses a trivial object format in which each
|
||||
* object contains a single reference.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mps.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscamc.h"
|
||||
#include "testlib.h"
|
||||
|
||||
enum {
|
||||
TYPE_REF,
|
||||
TYPE_FWD,
|
||||
TYPE_PAD
|
||||
};
|
||||
|
||||
typedef struct obj_s {
|
||||
unsigned type; /* One of the TYPE_ enums */
|
||||
union {
|
||||
struct obj_s *ref; /* TYPE_REF */
|
||||
mps_addr_t fwd; /* TYPE_FWD */
|
||||
size_t pad; /* TYPE_PAD */
|
||||
} u;
|
||||
} obj_s, *obj_t;
|
||||
|
||||
static void obj_fwd(mps_addr_t old, mps_addr_t new)
|
||||
{
|
||||
obj_t obj = old;
|
||||
obj->type = TYPE_FWD;
|
||||
obj->u.fwd = new;
|
||||
}
|
||||
|
||||
static mps_addr_t obj_isfwd(mps_addr_t addr)
|
||||
{
|
||||
obj_t obj = addr;
|
||||
if (obj->type == TYPE_FWD) {
|
||||
return obj->u.fwd;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void obj_pad(mps_addr_t addr, size_t size)
|
||||
{
|
||||
obj_t obj = addr;
|
||||
obj->type = TYPE_PAD;
|
||||
obj->u.pad = size;
|
||||
}
|
||||
|
||||
static mps_addr_t obj_skip(mps_addr_t addr)
|
||||
{
|
||||
obj_t obj = addr;
|
||||
size_t size;
|
||||
if (obj->type == TYPE_PAD) {
|
||||
size = obj->u.pad;
|
||||
} else {
|
||||
size = sizeof(obj_s);
|
||||
}
|
||||
return (char *)addr + size;
|
||||
}
|
||||
|
||||
static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
MPS_SCAN_BEGIN(ss) {
|
||||
while (base < limit) {
|
||||
obj_t obj = base;
|
||||
if (obj->type == TYPE_REF) {
|
||||
mps_addr_t p = obj->u.ref;
|
||||
mps_res_t res = MPS_FIX12(ss, &p);
|
||||
if (res != MPS_RES_OK) {
|
||||
return res;
|
||||
}
|
||||
obj->u.ref = p;
|
||||
}
|
||||
base = obj_skip(base);
|
||||
}
|
||||
} MPS_SCAN_END(ss);
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *marker = ▮
|
||||
int pid;
|
||||
mps_arena_t arena;
|
||||
mps_fmt_t obj_fmt;
|
||||
mps_pool_t pool;
|
||||
mps_thr_t thread;
|
||||
mps_root_t stack_root;
|
||||
mps_ap_t obj_ap;
|
||||
size_t i;
|
||||
obj_t obj, first;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
/* Set the pause time to be very small so that the incremental
|
||||
collector (when it runs) will have to leave a read barrier in
|
||||
place for us to hit. */
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, 0.0);
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args),
|
||||
"mps_arena_create");
|
||||
} MPS_ARGS_END(args);
|
||||
mps_arena_park(arena);
|
||||
|
||||
die(mps_thread_reg(&thread, arena), "Couldn't register thread");
|
||||
die(mps_root_create_thread(&stack_root, arena, thread, marker),
|
||||
"Couldn't create thread root");
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, sizeof(obj_s));
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, obj_scan);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, obj_skip);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_FWD, obj_fwd);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_ISFWD, obj_isfwd);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, obj_pad);
|
||||
die(mps_fmt_create_k(&obj_fmt, arena, args), "Couldn't create obj format");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
|
||||
die(mps_pool_create_k(&pool, arena, mps_class_amc(), args),
|
||||
"Couldn't create pool");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
die(mps_ap_create_k(&obj_ap, pool, mps_args_none),
|
||||
"Couldn't create obj allocation point");
|
||||
|
||||
/* Create a linked list of objects. */
|
||||
first = NULL;
|
||||
for (i = 0; i < 100000; ++i) {
|
||||
size_t size = sizeof(obj_s);
|
||||
mps_addr_t addr;
|
||||
do {
|
||||
die(mps_reserve(&addr, obj_ap, size), "Couldn't allocate.");
|
||||
obj = addr;
|
||||
obj->type = TYPE_REF;
|
||||
obj->u.ref = NULL;
|
||||
} while (!mps_commit(obj_ap, addr, size));
|
||||
obj->u.ref = first;
|
||||
first = obj;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
cdie(pid >= 0, "fork failed");
|
||||
|
||||
/* Allow a collection to start, which will cause a read barrier to
|
||||
be applied to any segment containing live objects that was
|
||||
scanned. */
|
||||
mps_arena_release(arena);
|
||||
|
||||
/* Read all the objects, so that if there is read barrier in place
|
||||
we will hit it. */
|
||||
for (obj = first; obj != NULL; obj = obj->u.ref) {
|
||||
Insist(obj->type == TYPE_REF);
|
||||
}
|
||||
|
||||
mps_arena_park(arena);
|
||||
|
||||
if (pid != 0) {
|
||||
/* Parent: wait for child and check that its exit status is zero. */
|
||||
int stat;
|
||||
cdie(pid == waitpid(pid, &stat, 0), "waitpid failed");
|
||||
cdie(WIFEXITED(stat), "child did not exit normally");
|
||||
cdie(WEXITSTATUS(stat) == 0, "child exited with nonzero status");
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
}
|
||||
|
||||
mps_ap_destroy(obj_ap);
|
||||
mps_pool_destroy(pool);
|
||||
mps_fmt_destroy(obj_fmt);
|
||||
mps_root_destroy(stack_root);
|
||||
mps_thread_dereg(thread);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2018-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
247
mps/code/format.c
Normal file
247
mps/code/format.c
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
/* format.c: OBJECT FORMATS
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (c) 2002 Global Graphics Software.
|
||||
*
|
||||
* DESIGN
|
||||
*
|
||||
* See protocol.mps.format.
|
||||
*/
|
||||
|
||||
#include "mpm.h"
|
||||
|
||||
SRCID(format, "$Id$");
|
||||
|
||||
|
||||
/* FormatCheck -- check a format */
|
||||
|
||||
Bool FormatCheck(Format format)
|
||||
{
|
||||
CHECKS(Format, format);
|
||||
CHECKU(Arena, format->arena);
|
||||
CHECKL(format->serial < format->arena->formatSerial);
|
||||
CHECKD_NOSIG(Ring, &format->arenaRing);
|
||||
CHECKL(AlignCheck(format->alignment));
|
||||
/* TODO: Define the concept of the maximum alignment it is possible to
|
||||
request from the MPS, document and provide an interface to it, and then
|
||||
check that this alignment is not greater than that, as well as all other
|
||||
alignments. */
|
||||
CHECKL(FUNCHECK(format->scan));
|
||||
CHECKL(FUNCHECK(format->skip));
|
||||
CHECKL(FUNCHECK(format->move));
|
||||
CHECKL(FUNCHECK(format->isMoved));
|
||||
CHECKL(FUNCHECK(format->pad));
|
||||
CHECKL(FUNCHECK(format->klass));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* FormatNo methods -- default values for format keyword arguments */
|
||||
|
||||
mps_res_t FormatNoScan(mps_ss_t mps_ss, mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
UNUSED(mps_ss);
|
||||
UNUSED(base);
|
||||
UNUSED(limit);
|
||||
NOTREACHED;
|
||||
return ResFAIL;
|
||||
}
|
||||
|
||||
static mps_addr_t FormatNoSkip(mps_addr_t object)
|
||||
{
|
||||
UNUSED(object);
|
||||
NOTREACHED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void FormatNoMove(mps_addr_t old, mps_addr_t new)
|
||||
{
|
||||
UNUSED(old);
|
||||
UNUSED(new);
|
||||
NOTREACHED;
|
||||
}
|
||||
|
||||
static mps_addr_t FormatNoIsMoved(mps_addr_t object)
|
||||
{
|
||||
UNUSED(object);
|
||||
NOTREACHED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void FormatNoPad(mps_addr_t addr, size_t size)
|
||||
{
|
||||
UNUSED(addr);
|
||||
UNUSED(size);
|
||||
NOTREACHED;
|
||||
}
|
||||
|
||||
static mps_addr_t FormatDefaultClass(mps_addr_t object)
|
||||
{
|
||||
AVER(object != NULL);
|
||||
|
||||
return ((mps_addr_t *)object)[0];
|
||||
}
|
||||
|
||||
|
||||
/* FormatCreate -- create a format */
|
||||
|
||||
ARG_DEFINE_KEY(FMT_ALIGN, Align);
|
||||
ARG_DEFINE_KEY(FMT_SCAN, Fun);
|
||||
ARG_DEFINE_KEY(FMT_SKIP, Fun);
|
||||
ARG_DEFINE_KEY(FMT_FWD, Fun);
|
||||
ARG_DEFINE_KEY(FMT_ISFWD, Fun);
|
||||
ARG_DEFINE_KEY(FMT_PAD, Fun);
|
||||
ARG_DEFINE_KEY(FMT_HEADER_SIZE, Size);
|
||||
ARG_DEFINE_KEY(FMT_CLASS, Fun);
|
||||
|
||||
Res FormatCreate(Format *formatReturn, Arena arena, ArgList args)
|
||||
{
|
||||
ArgStruct arg;
|
||||
Format format;
|
||||
Res res;
|
||||
void *p;
|
||||
Align fmtAlign = FMT_ALIGN_DEFAULT;
|
||||
Size fmtHeaderSize = FMT_HEADER_SIZE_DEFAULT;
|
||||
mps_fmt_scan_t fmtScan = FMT_SCAN_DEFAULT;
|
||||
mps_fmt_skip_t fmtSkip = FMT_SKIP_DEFAULT;
|
||||
mps_fmt_fwd_t fmtFwd = FMT_FWD_DEFAULT;
|
||||
mps_fmt_isfwd_t fmtIsfwd = FMT_ISFWD_DEFAULT;
|
||||
mps_fmt_pad_t fmtPad = FMT_PAD_DEFAULT;
|
||||
mps_fmt_class_t fmtClass = FMT_CLASS_DEFAULT;
|
||||
|
||||
AVER(formatReturn != NULL);
|
||||
AVERT(Arena, arena);
|
||||
AVERT(ArgList, args);
|
||||
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_ALIGN))
|
||||
fmtAlign = arg.val.align;
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_HEADER_SIZE))
|
||||
fmtHeaderSize = arg.val.size;
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_SCAN))
|
||||
fmtScan = arg.val.fmt_scan;
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_SKIP))
|
||||
fmtSkip = arg.val.fmt_skip;
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_FWD))
|
||||
fmtFwd = arg.val.fmt_fwd;
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_ISFWD))
|
||||
fmtIsfwd = arg.val.fmt_isfwd;
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_PAD))
|
||||
fmtPad = arg.val.fmt_pad;
|
||||
if (ArgPick(&arg, args, MPS_KEY_FMT_CLASS))
|
||||
fmtClass = arg.val.fmt_class;
|
||||
|
||||
res = ControlAlloc(&p, arena, sizeof(FormatStruct));
|
||||
if(res != ResOK)
|
||||
return res;
|
||||
format = (Format)p; /* avoid pun */
|
||||
|
||||
format->arena = arena;
|
||||
RingInit(&format->arenaRing);
|
||||
format->poolCount = 0;
|
||||
format->alignment = fmtAlign;
|
||||
format->headerSize = fmtHeaderSize;
|
||||
format->scan = fmtScan;
|
||||
format->skip = fmtSkip;
|
||||
format->move = fmtFwd;
|
||||
format->isMoved = fmtIsfwd;
|
||||
format->pad = fmtPad;
|
||||
format->klass = fmtClass;
|
||||
|
||||
format->sig = FormatSig;
|
||||
format->serial = arena->formatSerial;
|
||||
++arena->formatSerial;
|
||||
|
||||
AVERT(Format, format);
|
||||
|
||||
RingAppend(&arena->formatRing, &format->arenaRing);
|
||||
|
||||
*formatReturn = format;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* FormatDestroy -- destroy a format */
|
||||
|
||||
void FormatDestroy(Format format)
|
||||
{
|
||||
AVERT(Format, format);
|
||||
AVER(format->poolCount == 0); /* <design/check/#.common> */
|
||||
|
||||
RingRemove(&format->arenaRing);
|
||||
|
||||
format->sig = SigInvalid;
|
||||
|
||||
RingFinish(&format->arenaRing);
|
||||
|
||||
ControlFree(format->arena, format, sizeof(FormatStruct));
|
||||
}
|
||||
|
||||
|
||||
/* FormatArena -- find the arena of a format
|
||||
*
|
||||
* Must be thread-safe. <design/interface-c#.check.testt>. */
|
||||
|
||||
Arena FormatArena(Format format)
|
||||
{
|
||||
AVER(TESTT(Format, format));
|
||||
return format->arena;
|
||||
}
|
||||
|
||||
|
||||
/* FormatDescribe -- describe a format */
|
||||
|
||||
Res FormatDescribe(Format format, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
Res res;
|
||||
|
||||
res = WriteF(stream, depth,
|
||||
"Format $P ($U) {\n", (WriteFP)format, (WriteFU)format->serial,
|
||||
" arena $P ($U)\n",
|
||||
(WriteFP)format->arena, (WriteFU)format->arena->serial,
|
||||
" poolCount $U\n", (WriteFU)format->poolCount,
|
||||
" alignment $W\n", (WriteFW)format->alignment,
|
||||
" scan $F\n", (WriteFF)format->scan,
|
||||
" skip $F\n", (WriteFF)format->skip,
|
||||
" move $F\n", (WriteFF)format->move,
|
||||
" isMoved $F\n", (WriteFF)format->isMoved,
|
||||
" pad $F\n", (WriteFF)format->pad,
|
||||
" headerSize $W\n", (WriteFW)format->headerSize,
|
||||
"} Format $P ($U)\n", (WriteFP)format, (WriteFU)format->serial,
|
||||
NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
250
mps/code/fotest.c
Normal file
250
mps/code/fotest.c
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/* fotest.c: FAIL-OVER TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
* Portions copyright (C) 2002 Global Graphics Software.
|
||||
*
|
||||
* This tests fail-over behaviour in low memory situations. The MVFF
|
||||
* and MVT pool classes normally maintain their list of free blocks in
|
||||
* a Coalescing Block Structure (CBS), but if the CBS cannot handle a
|
||||
* request due to running out of memory, they fall back to a Freelist
|
||||
* (which has zero memory overhead, at some cost in performance).
|
||||
*
|
||||
* This is a white box test: it monkey-patches the MFS pool's alloc
|
||||
* method with a method that always returns a memory error code.
|
||||
*/
|
||||
|
||||
|
||||
#include "mpscmvff.h"
|
||||
#include "mpscmvt.h"
|
||||
#include "mpsavm.h"
|
||||
|
||||
#include "testlib.h"
|
||||
|
||||
#include "cbs.h"
|
||||
#include "mpm.h"
|
||||
#include "mpmst.h"
|
||||
#include "mpmtypes.h"
|
||||
#include "poolmfs.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
|
||||
|
||||
#define testArenaSIZE ((((size_t)3)<<24) - 4)
|
||||
#define testSetSIZE 200 /* TODO: 10 * arena grain size / sizeof cbs_struct */
|
||||
#define testLOOPS 10
|
||||
|
||||
|
||||
/* make -- allocate one object */
|
||||
|
||||
static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size)
|
||||
{
|
||||
mps_res_t res;
|
||||
|
||||
do {
|
||||
MPS_RESERVE_BLOCK(res, *p, ap, size);
|
||||
if(res != MPS_RES_OK)
|
||||
return res;
|
||||
} while(!mps_commit(ap, *p, size));
|
||||
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
|
||||
/* The original alloc method on the MFS pool. */
|
||||
static PoolAllocMethod mfs_alloc;
|
||||
|
||||
|
||||
/* Are we currently in a part of the test that is allowed to fail in the case
|
||||
* where we run out of memory? This controls the behaviour of oomAlloc. */
|
||||
static Bool simulate_allocation_failure = FALSE;
|
||||
|
||||
/* How many times has oomAlloc failed on purpose. */
|
||||
static unsigned long failure_count = 0;
|
||||
|
||||
/* oomAlloc -- allocation function that reliably fails
|
||||
*
|
||||
* Returns a randomly chosen memory error code (and increments
|
||||
* `failure_count`) if `simulate_allocation_failure`. The point is to verify
|
||||
* that none of these errors affects the caller. */
|
||||
|
||||
static Res oomAlloc(Addr *pReturn, Pool pool, Size size)
|
||||
{
|
||||
if (simulate_allocation_failure) {
|
||||
/* Simulate a single failure in order to enforce the fail-over behaviour. */
|
||||
++ failure_count;
|
||||
simulate_allocation_failure = 0;
|
||||
switch (rnd() % 3) {
|
||||
case 0:
|
||||
return ResRESOURCE;
|
||||
case 1:
|
||||
return ResMEMORY;
|
||||
default:
|
||||
return ResCOMMIT_LIMIT;
|
||||
}
|
||||
} else {
|
||||
/* Failure here is allowed, so attempt allocation as normal.
|
||||
* (see job004041 and job004104). */
|
||||
return mfs_alloc(pReturn, pool, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* stress -- create an allocation point and allocate in it */
|
||||
|
||||
static mps_res_t stress(size_t (*size)(unsigned long, mps_align_t),
|
||||
mps_align_t alignment, mps_pool_t pool)
|
||||
{
|
||||
mps_res_t res = MPS_RES_OK;
|
||||
mps_ap_t ap;
|
||||
unsigned long i, k;
|
||||
int *ps[testSetSIZE];
|
||||
size_t ss[testSetSIZE];
|
||||
|
||||
die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate");
|
||||
|
||||
/* allocate a load of objects */
|
||||
for (i=0; i<testSetSIZE; ++i) {
|
||||
mps_addr_t obj;
|
||||
ss[i] = (*size)(i, alignment);
|
||||
res = make(&obj, ap, ss[i]);
|
||||
if (res != MPS_RES_OK)
|
||||
goto allocFail;
|
||||
ps[i] = obj;
|
||||
if (ss[i] >= sizeof(ps[i]))
|
||||
*ps[i] = 1; /* Write something, so it gets swap. */
|
||||
}
|
||||
|
||||
failure_count = 0;
|
||||
|
||||
for (k=0; k<testLOOPS; ++k) {
|
||||
/* Use oomAlloc for the first iteration and then with 0.5 probability. */
|
||||
CLASS_STATIC(MFSPool).alloc = (k>0 && rnd() % 2) ? mfs_alloc : oomAlloc;
|
||||
|
||||
/* shuffle all the objects */
|
||||
for (i=0; i<testSetSIZE; ++i) {
|
||||
unsigned long j = i + rnd()%(testSetSIZE-i);
|
||||
void *tp;
|
||||
size_t ts;
|
||||
|
||||
tp = ps[j]; ts = ss[j];
|
||||
ps[j] = ps[i]; ss[j] = ss[i];
|
||||
ps[i] = tp; ss[i] = ts;
|
||||
}
|
||||
|
||||
/* free half of the objects */
|
||||
/* upper half, as when allocating them again we want smaller objects */
|
||||
/* see randomSize() */
|
||||
for (i=testSetSIZE/2; i<testSetSIZE; ++i) {
|
||||
simulate_allocation_failure = TRUE;
|
||||
mps_free(pool, (mps_addr_t)ps[i], ss[i]);
|
||||
simulate_allocation_failure = FALSE;
|
||||
/* if (i == testSetSIZE/2) */
|
||||
/* PoolDescribe((Pool)pool, mps_lib_stdout); */
|
||||
}
|
||||
|
||||
/* allocate some new objects */
|
||||
for (i=testSetSIZE/2; i<testSetSIZE; ++i) {
|
||||
mps_addr_t obj;
|
||||
ss[i] = (*size)(i, alignment);
|
||||
res = make(&obj, ap, ss[i]);
|
||||
if (res != MPS_RES_OK)
|
||||
goto allocFail;
|
||||
ps[i] = obj;
|
||||
}
|
||||
}
|
||||
CLASS_STATIC(MFSPool).alloc = mfs_alloc;
|
||||
|
||||
Insist(failure_count > 0);
|
||||
|
||||
allocFail:
|
||||
mps_ap_destroy(ap);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* randomSizeAligned -- produce sizes both large and small,
|
||||
* aligned by platform alignment */
|
||||
|
||||
static size_t randomSizeAligned(unsigned long i, mps_align_t alignment)
|
||||
{
|
||||
size_t maxSize = 2 * 160 * 0x2000;
|
||||
/* Reduce by a factor of 2 every 10 cycles. Total allocation about 40 MB. */
|
||||
return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, alignment);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_pool_t pool;
|
||||
mps_align_t alignment;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"mps_arena_create");
|
||||
mfs_alloc = CLASS_STATIC(MFSPool).alloc;
|
||||
alignment = sizeof(void *) << (rnd() % 4);
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, (64 + rnd() % 64) * 1024);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, (1 + rnd() % 8) * 8);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, alignment);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVFF_ARENA_HIGH, rnd() % 2);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVFF_SLOT_HIGH, rnd() % 2);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVFF_FIRST_FIT, rnd() % 2);
|
||||
die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "create MVFF");
|
||||
} MPS_ARGS_END(args);
|
||||
die(stress(randomSizeAligned, alignment, pool), "stress MVFF");
|
||||
mps_pool_destroy(pool);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"mps_arena_create");
|
||||
alignment = sizeof(void *) << (rnd() % 4);
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ALIGN, alignment);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MIN_SIZE, (1 + rnd() % 4) * 4);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, (1 + rnd() % 8) * 16);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MAX_SIZE, (1 + rnd() % 4) * 1024);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVT_RESERVE_DEPTH, (1 + rnd() % 64) * 16);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MVT_FRAG_LIMIT, (rnd() % 101) / 100.0);
|
||||
die(mps_pool_create_k(&pool, arena, mps_class_mvt(), args), "create MVT");
|
||||
} MPS_ARGS_END(args);
|
||||
die(stress(randomSizeAligned, alignment, pool), "stress MVT");
|
||||
mps_pool_destroy(pool);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
830
mps/code/freelist.c
Normal file
830
mps/code/freelist.c
Normal file
|
|
@ -0,0 +1,830 @@
|
|||
/* freelist.c: FREE LIST ALLOCATOR IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2013-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .sources: <design/freelist>.
|
||||
*/
|
||||
|
||||
#include "freelist.h"
|
||||
#include "mpm.h"
|
||||
#include "range.h"
|
||||
|
||||
SRCID(freelist, "$Id$");
|
||||
|
||||
|
||||
#define freelistAlignment(fl) LandAlignment(FreelistLand(fl))
|
||||
|
||||
|
||||
typedef union FreelistBlockUnion {
|
||||
struct FreelistBlockSmall {
|
||||
FreelistBlock next; /* tagged with low bit 1 */
|
||||
/* limit is (char *)this + freelistAlignment(fl) */
|
||||
} small;
|
||||
struct FreelistBlockLarge {
|
||||
FreelistBlock next; /* not tagged (low bit 0) */
|
||||
Addr limit;
|
||||
} large;
|
||||
} FreelistBlockUnion;
|
||||
|
||||
|
||||
/* freelistEND -- the end of a list
|
||||
*
|
||||
* The end of a list should not be represented with NULL, as this is
|
||||
* ambiguous. However, freelistEND is in fact a null pointer, for
|
||||
* performance. To check whether you have it right, try temporarily
|
||||
* defining freelistEND as ((FreelistBlock)2) or similar (it must be
|
||||
* an even number because of the use of a tag).
|
||||
*/
|
||||
|
||||
#define freelistEND ((FreelistBlock)0)
|
||||
|
||||
|
||||
/* freelistTag -- return the tag of word */
|
||||
|
||||
#define freelistTag(word) ((word) & 1)
|
||||
|
||||
|
||||
/* freelistTagSet -- return word updated with the tag set */
|
||||
|
||||
#define freelistTagSet(word) ((FreelistBlock)((Word)(word) | 1))
|
||||
|
||||
|
||||
/* freelistTagReset -- return word updated with the tag reset */
|
||||
|
||||
#define freelistTagReset(word) ((FreelistBlock)((Word)(word) & ~(Word)1))
|
||||
|
||||
|
||||
/* freelistTagCopy -- return 'to' updated to have the same tag as 'from' */
|
||||
|
||||
#define freelistTagCopy(to, from) ((FreelistBlock)((Word)(to) | freelistTag((Word)(from))))
|
||||
|
||||
|
||||
/* freelistBlockIsSmall -- return true if block is small, false if large */
|
||||
|
||||
#define freelistBlockIsSmall(block) freelistTag((Word)((block)->small.next))
|
||||
|
||||
|
||||
/* freelistBlockBase -- return the base of a block. */
|
||||
|
||||
#define freelistBlockBase(block) ((Addr)(block))
|
||||
|
||||
|
||||
/* freelistBlockNext -- return the next block in the list, or
|
||||
* freelistEND if there are no more blocks.
|
||||
*/
|
||||
|
||||
#define freelistBlockNext(block) freelistTagReset((block)->small.next)
|
||||
|
||||
|
||||
/* freelistBlockLimit -- return the limit of a block. */
|
||||
|
||||
static Addr freelistBlockLimit(Freelist fl, FreelistBlock block)
|
||||
{
|
||||
AVERT(Freelist, fl);
|
||||
if (freelistBlockIsSmall(block)) {
|
||||
return AddrAdd(freelistBlockBase(block), freelistAlignment(fl));
|
||||
} else {
|
||||
return block->large.limit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* FreelistBlockCheck -- check a block. */
|
||||
|
||||
ATTRIBUTE_UNUSED
|
||||
static Bool FreelistBlockCheck(FreelistBlock block)
|
||||
{
|
||||
CHECKL(block != NULL);
|
||||
/* block list is address-ordered */
|
||||
CHECKL(freelistBlockNext(block) == freelistEND
|
||||
|| block < freelistBlockNext(block));
|
||||
CHECKL(freelistBlockIsSmall(block) || (Addr)block < block->large.limit);
|
||||
/* Would like to CHECKL(!freelistBlockIsSmall(block) ||
|
||||
* freelistBlockSize(fl, block) == freelistAlignment(fl)) but we
|
||||
* don't have 'fl' here. This is checked in freelistBlockSetLimit. */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* freelistBlockSize -- return the size of a block. */
|
||||
|
||||
#define freelistBlockSize(fl, block) \
|
||||
AddrOffset(freelistBlockBase(block), freelistBlockLimit(fl, block))
|
||||
|
||||
|
||||
/* freelistBlockSetNext -- update the next block in the list */
|
||||
|
||||
static void freelistBlockSetNext(FreelistBlock block, FreelistBlock next)
|
||||
{
|
||||
AVERT(FreelistBlock, block);
|
||||
block->small.next = freelistTagCopy(next, block->small.next);
|
||||
}
|
||||
|
||||
|
||||
/* freelistBlockSetLimit -- update the limit of a block */
|
||||
|
||||
static void freelistBlockSetLimit(Freelist fl, FreelistBlock block, Addr limit)
|
||||
{
|
||||
Size size;
|
||||
|
||||
AVERT(Freelist, fl);
|
||||
AVERT(FreelistBlock, block);
|
||||
AVER(AddrIsAligned(limit, freelistAlignment(fl)));
|
||||
AVER(freelistBlockBase(block) < limit);
|
||||
|
||||
size = AddrOffset(block, limit);
|
||||
if (size >= sizeof(block->large)) {
|
||||
block->large.next = freelistTagReset(block->large.next);
|
||||
block->large.limit = limit;
|
||||
} else {
|
||||
AVER(size >= sizeof(block->small));
|
||||
block->small.next = freelistTagSet(block->small.next);
|
||||
AVER(freelistBlockSize(fl, block) == freelistAlignment(fl));
|
||||
}
|
||||
AVER(freelistBlockLimit(fl, block) == limit);
|
||||
}
|
||||
|
||||
|
||||
/* freelistBlockInit -- initialize block storing the range [base, limit). */
|
||||
|
||||
static FreelistBlock freelistBlockInit(Freelist fl, Addr base, Addr limit)
|
||||
{
|
||||
FreelistBlock block;
|
||||
|
||||
AVERT(Freelist, fl);
|
||||
AVER(base != NULL);
|
||||
AVER(AddrIsAligned(base, freelistAlignment(fl)));
|
||||
AVER(base < limit);
|
||||
AVER(AddrIsAligned(limit, freelistAlignment(fl)));
|
||||
|
||||
block = (FreelistBlock)base;
|
||||
block->small.next = freelistTagSet(freelistEND);
|
||||
freelistBlockSetLimit(fl, block, limit);
|
||||
AVERT(FreelistBlock, block);
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
Bool FreelistCheck(Freelist fl)
|
||||
{
|
||||
Land land;
|
||||
CHECKS(Freelist, fl);
|
||||
land = FreelistLand(fl);
|
||||
CHECKD(Land, land);
|
||||
CHECKL(AlignCheck(FreelistMinimumAlignment));
|
||||
CHECKL(sizeof(struct FreelistBlockSmall) < sizeof(struct FreelistBlockLarge));
|
||||
CHECKL(sizeof(struct FreelistBlockSmall) <= freelistAlignment(fl));
|
||||
/* <design/freelist#.impl.grain.align> */
|
||||
CHECKL(AlignIsAligned(freelistAlignment(fl), FreelistMinimumAlignment));
|
||||
CHECKL((fl->list == freelistEND) == (fl->listSize == 0));
|
||||
CHECKL((fl->list == freelistEND) == (fl->size == 0));
|
||||
CHECKL(SizeIsAligned(fl->size, freelistAlignment(fl)));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static Res freelistInit(Land land, Arena arena, Align alignment, ArgList args)
|
||||
{
|
||||
Freelist fl;
|
||||
Res res;
|
||||
|
||||
AVER(land != NULL);
|
||||
res = NextMethod(Land, Freelist, init)(land, arena, alignment, args);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
fl = CouldBeA(Freelist, land);
|
||||
|
||||
/* <design/freelist#.impl.grain> */
|
||||
AVER(AlignIsAligned(LandAlignment(land), FreelistMinimumAlignment));
|
||||
|
||||
fl->list = freelistEND;
|
||||
fl->listSize = 0;
|
||||
fl->size = 0;
|
||||
|
||||
SetClassOfPoly(land, CLASS(Freelist));
|
||||
fl->sig = FreelistSig;
|
||||
AVERC(Freelist, fl);
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
static void freelistFinish(Inst inst)
|
||||
{
|
||||
Land land = MustBeA(Land, inst);
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
fl->sig = SigInvalid;
|
||||
fl->list = freelistEND;
|
||||
NextMethod(Inst, Freelist, finish)(inst);
|
||||
}
|
||||
|
||||
|
||||
static Size freelistSize(Land land)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
return fl->size;
|
||||
}
|
||||
|
||||
|
||||
/* freelistBlockSetPrevNext -- update list of blocks
|
||||
*
|
||||
* If prev and next are both freelistEND, make the block list empty.
|
||||
* Otherwise, if prev is freelistEND, make next the first block in the list.
|
||||
* Otherwise, if next is freelistEND, make prev the last block in the list.
|
||||
* Otherwise, make next follow prev in the list.
|
||||
* Update the count of blocks by 'delta'.
|
||||
*
|
||||
* It is tempting to try to simplify this code by putting a
|
||||
* FreelistBlockUnion into the FreelistStruct and so avoiding the
|
||||
* special case on prev. But the problem with that idea is that we
|
||||
* can't guarantee that such a sentinel would respect the isolated
|
||||
* range invariant (it would have to be at a lower address than the
|
||||
* first block in the free list, which the MPS has no mechanism to
|
||||
* enforce), and so it would still have to be special-cased.
|
||||
*/
|
||||
|
||||
static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev,
|
||||
FreelistBlock next, int delta)
|
||||
{
|
||||
AVERT(Freelist, fl);
|
||||
|
||||
if (prev == freelistEND) {
|
||||
fl->list = next;
|
||||
} else {
|
||||
/* Isolated range invariant <design/freelist#.impl.invariant>. */
|
||||
AVER(next == freelistEND
|
||||
|| freelistBlockLimit(fl, prev) < freelistBlockBase(next));
|
||||
freelistBlockSetNext(prev, next);
|
||||
}
|
||||
if (delta < 0) {
|
||||
AVER(fl->listSize >= (Count)-delta);
|
||||
fl->listSize -= (Count)-delta;
|
||||
} else {
|
||||
fl->listSize += (Count)delta;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Res freelistInsert(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
FreelistBlock prev, cur, next, new;
|
||||
Addr base, limit;
|
||||
Bool coalesceLeft, coalesceRight;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERT(Range, range);
|
||||
AVER(RangeIsAligned(range, freelistAlignment(fl)));
|
||||
|
||||
base = RangeBase(range);
|
||||
limit = RangeLimit(range);
|
||||
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur != freelistEND) {
|
||||
if (base < freelistBlockLimit(fl, cur) && freelistBlockBase(cur) < limit)
|
||||
return ResFAIL; /* range overlaps with cur */
|
||||
if (limit <= freelistBlockBase(cur))
|
||||
break;
|
||||
next = freelistBlockNext(cur);
|
||||
if (next != freelistEND)
|
||||
/* Isolated range invariant <design/freelist#.impl.invariant>. */
|
||||
AVER(freelistBlockLimit(fl, cur) < freelistBlockBase(next));
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
/* Now we know that range does not overlap with any block, and if it
|
||||
* coalesces then it does so with prev on the left, and cur on the
|
||||
* right.
|
||||
*/
|
||||
coalesceLeft = (prev != freelistEND && base == freelistBlockLimit(fl, prev));
|
||||
coalesceRight = (cur != freelistEND && limit == freelistBlockBase(cur));
|
||||
|
||||
if (coalesceLeft && coalesceRight) {
|
||||
base = freelistBlockBase(prev);
|
||||
limit = freelistBlockLimit(fl, cur);
|
||||
freelistBlockSetLimit(fl, prev, limit);
|
||||
freelistBlockSetPrevNext(fl, prev, freelistBlockNext(cur), -1);
|
||||
|
||||
} else if (coalesceLeft) {
|
||||
base = freelistBlockBase(prev);
|
||||
freelistBlockSetLimit(fl, prev, limit);
|
||||
|
||||
} else if (coalesceRight) {
|
||||
next = freelistBlockNext(cur);
|
||||
limit = freelistBlockLimit(fl, cur);
|
||||
cur = freelistBlockInit(fl, base, limit);
|
||||
freelistBlockSetNext(cur, next);
|
||||
freelistBlockSetPrevNext(fl, prev, cur, 0);
|
||||
|
||||
} else {
|
||||
/* failed to coalesce: add new block */
|
||||
new = freelistBlockInit(fl, base, limit);
|
||||
freelistBlockSetNext(new, cur);
|
||||
freelistBlockSetPrevNext(fl, prev, new, +1);
|
||||
}
|
||||
|
||||
fl->size += RangeSize(range);
|
||||
RangeInit(rangeReturn, base, limit);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* freelistDeleteFromBlock -- delete range from block
|
||||
*
|
||||
* range must be a subset of block. Update rangeReturn to be the
|
||||
* original range of block and update the block list accordingly: prev
|
||||
* is on the list just before block, or freelistEND if block is the
|
||||
* first block on the list.
|
||||
*/
|
||||
|
||||
static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl,
|
||||
Range range, FreelistBlock prev,
|
||||
FreelistBlock block)
|
||||
{
|
||||
FreelistBlock next, new;
|
||||
Addr base, limit, blockBase, blockLimit;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERT(Freelist, fl);
|
||||
AVERT(Range, range);
|
||||
AVER(RangeIsAligned(range, freelistAlignment(fl)));
|
||||
AVER(prev == freelistEND || freelistBlockNext(prev) == block);
|
||||
AVERT(FreelistBlock, block);
|
||||
AVER(freelistBlockBase(block) <= RangeBase(range));
|
||||
AVER(RangeLimit(range) <= freelistBlockLimit(fl, block));
|
||||
|
||||
base = RangeBase(range);
|
||||
limit = RangeLimit(range);
|
||||
blockBase = freelistBlockBase(block);
|
||||
blockLimit = freelistBlockLimit(fl, block);
|
||||
next = freelistBlockNext(block);
|
||||
|
||||
if (base == blockBase && limit == blockLimit) {
|
||||
/* No fragment at left; no fragment at right. */
|
||||
freelistBlockSetPrevNext(fl, prev, next, -1);
|
||||
|
||||
} else if (base == blockBase) {
|
||||
/* No fragment at left; block at right. */
|
||||
block = freelistBlockInit(fl, limit, blockLimit);
|
||||
freelistBlockSetNext(block, next);
|
||||
freelistBlockSetPrevNext(fl, prev, block, 0);
|
||||
|
||||
} else if (limit == blockLimit) {
|
||||
/* Block at left; no fragment at right. */
|
||||
freelistBlockSetLimit(fl, block, base);
|
||||
|
||||
} else {
|
||||
/* Block at left; block at right. */
|
||||
freelistBlockSetLimit(fl, block, base);
|
||||
new = freelistBlockInit(fl, limit, blockLimit);
|
||||
freelistBlockSetNext(new, next);
|
||||
freelistBlockSetPrevNext(fl, block, new, +1);
|
||||
}
|
||||
|
||||
AVER(fl->size >= RangeSize(range));
|
||||
fl->size -= RangeSize(range);
|
||||
RangeInit(rangeReturn, blockBase, blockLimit);
|
||||
}
|
||||
|
||||
|
||||
static Res freelistDelete(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
FreelistBlock prev, cur, next;
|
||||
Addr base, limit;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERT(Range, range);
|
||||
|
||||
base = RangeBase(range);
|
||||
limit = RangeLimit(range);
|
||||
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur != freelistEND) {
|
||||
Addr blockBase, blockLimit;
|
||||
blockBase = freelistBlockBase(cur);
|
||||
blockLimit = freelistBlockLimit(fl, cur);
|
||||
|
||||
if (limit <= blockBase)
|
||||
return ResFAIL; /* not found */
|
||||
if (base <= blockLimit) {
|
||||
if (base < blockBase || blockLimit < limit)
|
||||
return ResFAIL; /* partially overlapping */
|
||||
freelistDeleteFromBlock(rangeReturn, fl, range, prev, cur);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
next = freelistBlockNext(cur);
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
/* Range not found in block list. */
|
||||
return ResFAIL;
|
||||
}
|
||||
|
||||
|
||||
static Bool freelistIterate(Land land, LandVisitor visitor,
|
||||
void *closure)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
FreelistBlock cur, next;
|
||||
|
||||
AVER(FUNCHECK(visitor));
|
||||
/* closure arbitrary */
|
||||
|
||||
for (cur = fl->list; cur != freelistEND; cur = next) {
|
||||
RangeStruct range;
|
||||
Bool cont;
|
||||
/* .next.first: Take next before calling the visitor, in case the
|
||||
* visitor touches the block. */
|
||||
next = freelistBlockNext(cur);
|
||||
RangeInit(&range, freelistBlockBase(cur), freelistBlockLimit(fl, cur));
|
||||
cont = (*visitor)(land, &range, closure);
|
||||
if (!cont)
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static Bool freelistIterateAndDelete(Land land, LandDeleteVisitor visitor,
|
||||
void *closure)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
FreelistBlock prev, cur, next;
|
||||
|
||||
AVER(FUNCHECK(visitor));
|
||||
/* closure arbitrary */
|
||||
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur != freelistEND) {
|
||||
Bool delete = FALSE;
|
||||
RangeStruct range;
|
||||
Bool cont;
|
||||
Size size;
|
||||
next = freelistBlockNext(cur); /* See .next.first. */
|
||||
size = freelistBlockSize(fl, cur);
|
||||
RangeInit(&range, freelistBlockBase(cur), freelistBlockLimit(fl, cur));
|
||||
cont = (*visitor)(&delete, land, &range, closure);
|
||||
if (delete) {
|
||||
freelistBlockSetPrevNext(fl, prev, next, -1);
|
||||
AVER(fl->size >= size);
|
||||
fl->size -= size;
|
||||
} else {
|
||||
prev = cur;
|
||||
}
|
||||
if (!cont)
|
||||
return FALSE;
|
||||
cur = next;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* freelistFindDeleteFromBlock -- delete size bytes from block
|
||||
*
|
||||
* Find a chunk of size bytes in block (which is known to be at least
|
||||
* that big) and possibly delete that chunk according to the
|
||||
* instruction in findDelete. Return the range of that chunk in
|
||||
* rangeReturn. Return the original range of the block in
|
||||
* oldRangeReturn. Update the block list accordingly, using prev,
|
||||
* which is previous in list or freelistEND if block is the first
|
||||
* block in the list.
|
||||
*/
|
||||
|
||||
static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn,
|
||||
Freelist fl, Size size,
|
||||
FindDelete findDelete,
|
||||
FreelistBlock prev, FreelistBlock block)
|
||||
{
|
||||
Bool callDelete = TRUE;
|
||||
Addr base, limit;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(Freelist, fl);
|
||||
AVER(SizeIsAligned(size, freelistAlignment(fl)));
|
||||
AVERT(FindDelete, findDelete);
|
||||
AVER(prev == freelistEND || freelistBlockNext(prev) == block);
|
||||
AVERT(FreelistBlock, block);
|
||||
AVER(freelistBlockSize(fl, block) >= size);
|
||||
|
||||
base = freelistBlockBase(block);
|
||||
limit = freelistBlockLimit(fl, block);
|
||||
|
||||
switch (findDelete) {
|
||||
case FindDeleteNONE:
|
||||
callDelete = FALSE;
|
||||
break;
|
||||
|
||||
case FindDeleteLOW:
|
||||
limit = AddrAdd(base, size);
|
||||
break;
|
||||
|
||||
case FindDeleteHIGH:
|
||||
base = AddrSub(limit, size);
|
||||
break;
|
||||
|
||||
case FindDeleteENTIRE:
|
||||
/* do nothing */
|
||||
break;
|
||||
|
||||
default:
|
||||
NOTREACHED;
|
||||
break;
|
||||
}
|
||||
|
||||
RangeInit(rangeReturn, base, limit);
|
||||
if (callDelete) {
|
||||
freelistDeleteFromBlock(oldRangeReturn, fl, rangeReturn, prev, block);
|
||||
} else {
|
||||
RangeInit(oldRangeReturn, base, limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Bool freelistFindFirst(Range rangeReturn, Range oldRangeReturn,
|
||||
Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
FreelistBlock prev, cur, next;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVER(SizeIsAligned(size, freelistAlignment(fl)));
|
||||
AVERT(FindDelete, findDelete);
|
||||
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur != freelistEND) {
|
||||
if (freelistBlockSize(fl, cur) >= size) {
|
||||
freelistFindDeleteFromBlock(rangeReturn, oldRangeReturn, fl, size,
|
||||
findDelete, prev, cur);
|
||||
return TRUE;
|
||||
}
|
||||
next = freelistBlockNext(cur);
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static Bool freelistFindLast(Range rangeReturn, Range oldRangeReturn,
|
||||
Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
Bool found = FALSE;
|
||||
FreelistBlock prev, cur, next;
|
||||
FreelistBlock foundPrev = freelistEND, foundCur = freelistEND;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVER(SizeIsAligned(size, freelistAlignment(fl)));
|
||||
AVERT(FindDelete, findDelete);
|
||||
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur != freelistEND) {
|
||||
if (freelistBlockSize(fl, cur) >= size) {
|
||||
found = TRUE;
|
||||
foundPrev = prev;
|
||||
foundCur = cur;
|
||||
}
|
||||
next = freelistBlockNext(cur);
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
if (found)
|
||||
freelistFindDeleteFromBlock(rangeReturn, oldRangeReturn, fl, size,
|
||||
findDelete, foundPrev, foundCur);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn,
|
||||
Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
Bool found = FALSE;
|
||||
FreelistBlock prev, cur, next;
|
||||
FreelistBlock bestPrev = freelistEND, bestCur = freelistEND;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERT(FindDelete, findDelete);
|
||||
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur != freelistEND) {
|
||||
if (freelistBlockSize(fl, cur) >= size) {
|
||||
found = TRUE;
|
||||
size = freelistBlockSize(fl, cur);
|
||||
bestPrev = prev;
|
||||
bestCur = cur;
|
||||
}
|
||||
next = freelistBlockNext(cur);
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
if (found)
|
||||
freelistFindDeleteFromBlock(rangeReturn, oldRangeReturn, fl, size,
|
||||
findDelete, bestPrev, bestCur);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
static Res freelistFindInZones(Bool *foundReturn, Range rangeReturn,
|
||||
Range oldRangeReturn, Land land, Size size,
|
||||
ZoneSet zoneSet, Bool high)
|
||||
{
|
||||
Freelist fl = MustBeA(Freelist, land);
|
||||
LandFindMethod landFind;
|
||||
RangeInZoneSet search;
|
||||
Bool found = FALSE;
|
||||
FreelistBlock prev, cur, next;
|
||||
FreelistBlock foundPrev = freelistEND, foundCur = freelistEND;
|
||||
RangeStruct foundRange;
|
||||
|
||||
AVER(FALSE); /* TODO: this code is completely untested! */
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
/* AVERT(ZoneSet, zoneSet); */
|
||||
AVERT(Bool, high);
|
||||
|
||||
landFind = high ? freelistFindLast : freelistFindFirst;
|
||||
search = high ? RangeInZoneSetLast : RangeInZoneSetFirst;
|
||||
|
||||
if (zoneSet == ZoneSetEMPTY)
|
||||
goto fail;
|
||||
if (zoneSet == ZoneSetUNIV) {
|
||||
FindDelete fd = high ? FindDeleteHIGH : FindDeleteLOW;
|
||||
*foundReturn = (*landFind)(rangeReturn, oldRangeReturn, land, size, fd);
|
||||
return ResOK;
|
||||
}
|
||||
if (ZoneSetIsSingle(zoneSet) && size > ArenaStripeSize(LandArena(land)))
|
||||
goto fail;
|
||||
|
||||
prev = freelistEND;
|
||||
cur = fl->list;
|
||||
while (cur != freelistEND) {
|
||||
Addr base, limit;
|
||||
if ((*search)(&base, &limit, freelistBlockBase(cur),
|
||||
freelistBlockLimit(fl, cur),
|
||||
LandArena(land), zoneSet, size))
|
||||
{
|
||||
found = TRUE;
|
||||
foundPrev = prev;
|
||||
foundCur = cur;
|
||||
RangeInit(&foundRange, base, limit);
|
||||
if (!high)
|
||||
break;
|
||||
}
|
||||
next = freelistBlockNext(cur);
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
goto fail;
|
||||
|
||||
freelistDeleteFromBlock(oldRangeReturn, fl, &foundRange, foundPrev, foundCur);
|
||||
RangeCopy(rangeReturn, &foundRange);
|
||||
*foundReturn = TRUE;
|
||||
return ResOK;
|
||||
|
||||
fail:
|
||||
*foundReturn = FALSE;
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* freelistDescribeVisitor -- visitor method for freelistDescribe
|
||||
*
|
||||
* Writes a description of the range into the stream pointed to by
|
||||
* closure.
|
||||
*/
|
||||
|
||||
typedef struct FreelistDescribeClosureStruct {
|
||||
mps_lib_FILE *stream;
|
||||
Count depth;
|
||||
} FreelistDescribeClosureStruct, *FreelistDescribeClosure;
|
||||
|
||||
static Bool freelistDescribeVisitor(Land land, Range range,
|
||||
void *closure)
|
||||
{
|
||||
Res res;
|
||||
FreelistDescribeClosure my = closure;
|
||||
|
||||
if (!TESTT(Land, land))
|
||||
return FALSE;
|
||||
if (!RangeCheck(range))
|
||||
return FALSE;
|
||||
if (my->stream == NULL)
|
||||
return FALSE;
|
||||
|
||||
res = WriteF(my->stream, my->depth,
|
||||
"[$P,", (WriteFP)RangeBase(range),
|
||||
"$P)", (WriteFP)RangeLimit(range),
|
||||
" {$U}\n", (WriteFU)RangeSize(range),
|
||||
NULL);
|
||||
|
||||
return res == ResOK;
|
||||
}
|
||||
|
||||
|
||||
static Res freelistDescribe(Inst inst, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
Land land = CouldBeA(Land, inst);
|
||||
Freelist fl = CouldBeA(Freelist, land);
|
||||
Res res;
|
||||
Bool b;
|
||||
FreelistDescribeClosureStruct closure;
|
||||
|
||||
if (!TESTC(Freelist, fl))
|
||||
return ResPARAM;
|
||||
if (stream == NULL)
|
||||
return ResPARAM;
|
||||
|
||||
res = NextMethod(Inst, Freelist, describe)(inst, stream, depth);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
res = WriteF(stream, depth + 2,
|
||||
"listSize $U\n", (WriteFU)fl->listSize,
|
||||
"size $U\n", (WriteFU)fl->size,
|
||||
NULL);
|
||||
|
||||
closure.stream = stream;
|
||||
closure.depth = depth + 2;
|
||||
b = LandIterate(land, freelistDescribeVisitor, &closure);
|
||||
if (!b)
|
||||
return ResFAIL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
DEFINE_CLASS(Land, Freelist, klass)
|
||||
{
|
||||
INHERIT_CLASS(klass, Freelist, Land);
|
||||
klass->instClassStruct.describe = freelistDescribe;
|
||||
klass->instClassStruct.finish = freelistFinish;
|
||||
klass->size = sizeof(FreelistStruct);
|
||||
klass->init = freelistInit;
|
||||
klass->sizeMethod = freelistSize;
|
||||
klass->insert = freelistInsert;
|
||||
klass->insertSteal = freelistInsert; /* doesn't need to allocate */
|
||||
klass->delete = freelistDelete;
|
||||
klass->deleteSteal = freelistDelete; /* doesn't need to allocate */
|
||||
klass->iterate = freelistIterate;
|
||||
klass->iterateAndDelete = freelistIterateAndDelete;
|
||||
klass->findFirst = freelistFindFirst;
|
||||
klass->findLast = freelistFindLast;
|
||||
klass->findLargest = freelistFindLargest;
|
||||
klass->findInZones = freelistFindInZones;
|
||||
AVERT(LandClass, klass);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2013-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
57
mps/code/freelist.h
Normal file
57
mps/code/freelist.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/* freelist.h: FREE LIST ALLOCATOR INTERFACE
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2013-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .source: <design/freelist>.
|
||||
*/
|
||||
|
||||
#ifndef freelist_h
|
||||
#define freelist_h
|
||||
|
||||
#include "mpmtypes.h"
|
||||
#include "mpm.h"
|
||||
#include "protocol.h"
|
||||
|
||||
typedef struct FreelistStruct *Freelist;
|
||||
|
||||
#define FreelistLand(fl) (&(fl)->landStruct)
|
||||
|
||||
extern Bool FreelistCheck(Freelist freelist);
|
||||
|
||||
/* <design/freelist#.impl.grain.align> */
|
||||
#define FreelistMinimumAlignment ((Align)sizeof(FreelistBlock))
|
||||
|
||||
DECLARE_CLASS(Land, Freelist, Land);
|
||||
|
||||
#endif /* freelist.h */
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2013-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
61
mps/code/fri3gc.gmk
Normal file
61
mps/code/fri3gc.gmk
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# fri3gc.gmk: BUILD FOR FreeBSD/i386/GCC PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = fri3gc
|
||||
|
||||
MPMPF = \
|
||||
lockix.c \
|
||||
prmcanan.c \
|
||||
prmcfri3.c \
|
||||
prmcix.c \
|
||||
protix.c \
|
||||
protsgix.c \
|
||||
pthrdext.c \
|
||||
span.c \
|
||||
thix.c \
|
||||
vmix.c
|
||||
|
||||
LIBS = -lm -pthread
|
||||
|
||||
include gc.gmk
|
||||
|
||||
# For SQLite3.
|
||||
LINKFLAGS += -L/usr/local/lib
|
||||
CFLAGSCOMPILER += -I/usr/local/include
|
||||
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
61
mps/code/fri3ll.gmk
Normal file
61
mps/code/fri3ll.gmk
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# fri3ll.gmk: BUILD FOR FreeBSD/i386/GCC PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = fri3ll
|
||||
|
||||
MPMPF = \
|
||||
lockix.c \
|
||||
prmcanan.c \
|
||||
prmcfri3.c \
|
||||
prmcix.c \
|
||||
protix.c \
|
||||
protsgix.c \
|
||||
pthrdext.c \
|
||||
span.c \
|
||||
thix.c \
|
||||
vmix.c
|
||||
|
||||
LIBS = -lm -pthread
|
||||
|
||||
include ll.gmk
|
||||
|
||||
# For SQLite3.
|
||||
LINKFLAGS += -L/usr/local/lib
|
||||
CFLAGSCOMPILER += -I/usr/local/include
|
||||
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
61
mps/code/fri6gc.gmk
Normal file
61
mps/code/fri6gc.gmk
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# fri6gc.gmk: BUILD FOR FreeBSD/x86-64/GCC PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = fri6gc
|
||||
|
||||
MPMPF = \
|
||||
lockix.c \
|
||||
prmcanan.c \
|
||||
prmcfri6.c \
|
||||
prmcix.c \
|
||||
protix.c \
|
||||
protsgix.c \
|
||||
pthrdext.c \
|
||||
span.c \
|
||||
thix.c \
|
||||
vmix.c
|
||||
|
||||
LIBS = -lm -pthread
|
||||
|
||||
include gc.gmk
|
||||
|
||||
# For SQLite3.
|
||||
LINKFLAGS += -L/usr/local/lib
|
||||
CFLAGSCOMPILER += -I/usr/local/include
|
||||
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
61
mps/code/fri6ll.gmk
Normal file
61
mps/code/fri6ll.gmk
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# fri6ll.gmk: BUILD FOR FreeBSD/x86-64/Clang PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = fri6ll
|
||||
|
||||
MPMPF = \
|
||||
lockix.c \
|
||||
prmcanan.c \
|
||||
prmcfri6.c \
|
||||
prmcix.c \
|
||||
protix.c \
|
||||
protsgix.c \
|
||||
pthrdext.c \
|
||||
span.c \
|
||||
thix.c \
|
||||
vmix.c
|
||||
|
||||
LIBS = -lm -pthread
|
||||
|
||||
include ll.gmk
|
||||
|
||||
# For SQLite3.
|
||||
LINKFLAGS += -L/usr/local/lib
|
||||
CFLAGSCOMPILER += -I/usr/local/include
|
||||
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
79
mps/code/gc.gmk
Normal file
79
mps/code/gc.gmk
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# gc.gmk: GNUMAKEFILE FRAGMENT FOR GNU CC
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# This file is included by platform makefiles that use the GNU CC
|
||||
# compiler. It defines the compiler-specific variables that the
|
||||
# common makefile fragment (<code/comm.gmk>) requires.
|
||||
|
||||
CC = gcc
|
||||
CFLAGSDEBUG = -O -g3
|
||||
CFLAGSOPT = -O2 -g3
|
||||
|
||||
# Warnings that might be enabled by clients <design/config/#.warning.impl>.
|
||||
CFLAGSCOMPILER := \
|
||||
-Waggregate-return \
|
||||
-Wall \
|
||||
-Wcast-qual \
|
||||
-Werror \
|
||||
-Wextra \
|
||||
-Winline \
|
||||
-Wmissing-prototypes \
|
||||
-Wnested-externs \
|
||||
-Wpointer-arith \
|
||||
-Wshadow \
|
||||
-Wstrict-aliasing=2 \
|
||||
-Wstrict-prototypes \
|
||||
-Wswitch-default \
|
||||
-Wwrite-strings
|
||||
CFLAGSCOMPILERSTRICT := -std=c89 -pedantic
|
||||
|
||||
# A different set of compiler flags for less strict compilation, for
|
||||
# instance when we need to #include a third-party header file that
|
||||
# won't fly with -ansi -pedantic. Use sparingly!
|
||||
CFLAGSCOMPILERLAX :=
|
||||
|
||||
# gcc -MM generates a dependency line of the form:
|
||||
# thing.o : thing.c ...
|
||||
# The sed line converts this into:
|
||||
# <pfm>/<variety>/thing.o <pfm>/<variety>/thing.d : thing.c ...
|
||||
# If interrupted, this is liable to leave a zero-length file behind.
|
||||
|
||||
define gendep
|
||||
$(SHELL) -ec "$(CC) $(CFLAGSSTRICT) -MM $< | \
|
||||
sed '/:/s!$*.o!$(@D)/& $(@D)/$*.d!' > $@"
|
||||
[ -s $@ ] || rm -f $@
|
||||
endef
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
526
mps/code/gcbench.c
Normal file
526
mps/code/gcbench.c
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
/* gcbench.c -- "GC" Benchmark on ANSI C library
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2014-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* This is an allocation stress benchmark test for gc pools
|
||||
*/
|
||||
|
||||
#include "mps.c"
|
||||
#include "testlib.h"
|
||||
#include "testthr.h"
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mpm.h"
|
||||
|
||||
#ifdef MPS_OS_W3
|
||||
#include "getopt.h"
|
||||
#else
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h> /* fprintf, printf, putchars, sscanf, stderr, stdout */
|
||||
#include <stdlib.h> /* alloca, exit, EXIT_FAILURE, EXIT_SUCCESS, strtoul */
|
||||
#include <time.h> /* clock, CLOCKS_PER_SEC */
|
||||
|
||||
#define RESMUST(expr) \
|
||||
do { \
|
||||
mps_res_t res = (expr); \
|
||||
if (res != MPS_RES_OK) { \
|
||||
fprintf(stderr, #expr " returned %d\n", res); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static mps_arena_t arena;
|
||||
static mps_pool_t pool;
|
||||
static mps_fmt_t format;
|
||||
static mps_chain_t chain;
|
||||
|
||||
/* objNULL needs to be odd so that it's ignored in exactRoots. */
|
||||
#define objNULL ((obj_t)MPS_WORD_CONST(0xDECEA5ED))
|
||||
#define genLIMIT 100
|
||||
|
||||
static rnd_state_t seed = 0; /* random number seed */
|
||||
static unsigned nthreads = 1; /* threads */
|
||||
static unsigned niter = 5; /* iterations */
|
||||
static unsigned npass = 10; /* passes over tree */
|
||||
static size_t width = 2; /* width of tree nodes */
|
||||
static unsigned depth = 20; /* depth of tree */
|
||||
static double preuse = 0.2; /* probability of reuse */
|
||||
static double pupdate = 0.1; /* probability of update */
|
||||
static unsigned ngen = 0; /* number of generations specified */
|
||||
static mps_gen_param_s gen[genLIMIT]; /* generation parameters */
|
||||
static size_t arena_size = 256ul * 1024 * 1024; /* arena size */
|
||||
static size_t arena_grain_size = 1; /* arena grain size */
|
||||
static unsigned pinleaf = FALSE; /* are leaf objects pinned at start */
|
||||
static mps_bool_t zoned = TRUE; /* arena allocates using zones */
|
||||
static double pause_time = ARENA_DEFAULT_PAUSE_TIME; /* maximum pause time */
|
||||
static double spare = ARENA_SPARE_DEFAULT; /* spare commit fraction */
|
||||
|
||||
typedef struct gcthread_s *gcthread_t;
|
||||
|
||||
typedef void *(*gcthread_fn_t)(gcthread_t thread);
|
||||
|
||||
struct gcthread_s {
|
||||
testthr_t thread;
|
||||
mps_thr_t mps_thread;
|
||||
mps_root_t reg_root;
|
||||
mps_ap_t ap;
|
||||
gcthread_fn_t fn;
|
||||
};
|
||||
|
||||
typedef mps_word_t obj_t;
|
||||
|
||||
static obj_t mkvector(mps_ap_t ap, size_t n)
|
||||
{
|
||||
mps_word_t v;
|
||||
RESMUST(make_dylan_vector(&v, ap, n));
|
||||
return v;
|
||||
}
|
||||
|
||||
static obj_t aref(obj_t v, size_t i)
|
||||
{
|
||||
return DYLAN_VECTOR_SLOT(v, i);
|
||||
}
|
||||
|
||||
static void aset(obj_t v, size_t i, obj_t val)
|
||||
{
|
||||
DYLAN_VECTOR_SLOT(v, i) = val;
|
||||
}
|
||||
|
||||
/* mktree - make a tree of nodes with depth d. */
|
||||
static obj_t mktree(mps_ap_t ap, unsigned d, obj_t leaf)
|
||||
{
|
||||
obj_t tree;
|
||||
size_t i;
|
||||
if (d <= 0)
|
||||
return leaf;
|
||||
tree = mkvector(ap, width);
|
||||
for (i = 0; i < width; ++i) {
|
||||
aset(tree, i, mktree(ap, d - 1, leaf));
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
static obj_t random_subtree(obj_t tree, unsigned levels)
|
||||
{
|
||||
while(tree != objNULL && levels > 0) {
|
||||
tree = aref(tree, rnd() % width);
|
||||
--levels;
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/* new_tree - Make a new tree from an old tree.
|
||||
* The new tree is the same depth as the old tree and
|
||||
* reuses old nodes with probability preuse.
|
||||
* NOTE: If a new node is reused multiple times, the total size
|
||||
* will be smaller.
|
||||
* NOTE: Changing preuse will dramatically change how much work
|
||||
* is done. In particular, if preuse==1, the old tree is returned
|
||||
* unchanged. */
|
||||
static obj_t new_tree(mps_ap_t ap, obj_t oldtree, unsigned d)
|
||||
{
|
||||
obj_t subtree;
|
||||
size_t i;
|
||||
if (rnd_double() < preuse) {
|
||||
subtree = random_subtree(oldtree, depth - d);
|
||||
} else {
|
||||
if (d == 0)
|
||||
return objNULL;
|
||||
subtree = mkvector(ap, width);
|
||||
for (i = 0; i < width; ++i) {
|
||||
aset(subtree, i, new_tree(ap, oldtree, d - 1));
|
||||
}
|
||||
}
|
||||
return subtree;
|
||||
}
|
||||
|
||||
/* Update tree to be identical tree but with nodes reallocated
|
||||
* with probability pupdate. This avoids writing to vector slots
|
||||
* if unnecessary. */
|
||||
static obj_t update_tree(mps_ap_t ap, obj_t oldtree, unsigned d)
|
||||
{
|
||||
obj_t tree;
|
||||
size_t i;
|
||||
if (oldtree == objNULL || d == 0)
|
||||
return oldtree;
|
||||
if (rnd_double() < pupdate) {
|
||||
tree = mkvector(ap, width);
|
||||
for (i = 0; i < width; ++i) {
|
||||
aset(tree, i, update_tree(ap, aref(oldtree, i), d - 1));
|
||||
}
|
||||
} else {
|
||||
tree = oldtree;
|
||||
for (i = 0; i < width; ++i) {
|
||||
obj_t oldsubtree = aref(oldtree, i);
|
||||
obj_t subtree = update_tree(ap, oldsubtree, d - 1);
|
||||
if (subtree != oldsubtree) {
|
||||
aset(tree, i, subtree);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
static void *gc_tree(gcthread_t thread)
|
||||
{
|
||||
unsigned i, j;
|
||||
mps_ap_t ap = thread->ap;
|
||||
obj_t leaf = pinleaf ? mktree(ap, 1, objNULL) : objNULL;
|
||||
for (i = 0; i < niter; ++i) {
|
||||
obj_t tree = mktree(ap, depth, leaf);
|
||||
for (j = 0 ; j < npass; ++j) {
|
||||
if (preuse < 1.0)
|
||||
tree = new_tree(ap, tree, depth);
|
||||
if (pupdate > 0.0)
|
||||
tree = update_tree(ap, tree, depth);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* start -- start routine for each thread */
|
||||
static void *start(void *p)
|
||||
{
|
||||
gcthread_t thread = p;
|
||||
void *marker;
|
||||
RESMUST(mps_thread_reg(&thread->mps_thread, arena));
|
||||
RESMUST(mps_root_create_thread(&thread->reg_root, arena,
|
||||
thread->mps_thread, &marker));
|
||||
RESMUST(mps_ap_create_k(&thread->ap, pool, mps_args_none));
|
||||
thread->fn(thread);
|
||||
mps_ap_destroy(thread->ap);
|
||||
mps_root_destroy(thread->reg_root);
|
||||
mps_thread_dereg(thread->mps_thread);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void weave(gcthread_fn_t fn)
|
||||
{
|
||||
gcthread_t threads = alloca(sizeof(threads[0]) * nthreads);
|
||||
unsigned t;
|
||||
|
||||
for (t = 0; t < nthreads; ++t) {
|
||||
gcthread_t thread = &threads[t];
|
||||
thread->fn = fn;
|
||||
testthr_create(&thread->thread, start, thread);
|
||||
}
|
||||
|
||||
for (t = 0; t < nthreads; ++t)
|
||||
testthr_join(&threads[t].thread, NULL);
|
||||
}
|
||||
|
||||
static void weave1(gcthread_fn_t fn)
|
||||
{
|
||||
gcthread_t thread = alloca(sizeof(thread[0]));
|
||||
|
||||
thread->fn = fn;
|
||||
start(thread);
|
||||
}
|
||||
|
||||
|
||||
static void watch(gcthread_fn_t fn, const char *name)
|
||||
{
|
||||
clock_t begin, end;
|
||||
|
||||
begin = clock();
|
||||
if (nthreads == 1)
|
||||
weave1(fn);
|
||||
else
|
||||
weave(fn);
|
||||
end = clock();
|
||||
|
||||
printf("%s: %g\n", name, (double)(end - begin) / CLOCKS_PER_SEC);
|
||||
}
|
||||
|
||||
|
||||
/* Setup MPS arena and call benchmark. */
|
||||
|
||||
static void arena_setup(gcthread_fn_t fn,
|
||||
mps_pool_class_t pool_class,
|
||||
const char *name)
|
||||
{
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, zoned);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, pause_time);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_SPARE, spare);
|
||||
RESMUST(mps_arena_create_k(&arena, mps_arena_class_vm(), args));
|
||||
} MPS_ARGS_END(args);
|
||||
RESMUST(dylan_fmt(&format, arena));
|
||||
/* Make wrappers now to avoid race condition. */
|
||||
/* dylan_make_wrappers() uses malloc. */
|
||||
RESMUST(dylan_make_wrappers());
|
||||
if (ngen > 0)
|
||||
RESMUST(mps_chain_create(&chain, arena, ngen, gen));
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format);
|
||||
if (ngen > 0)
|
||||
MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain);
|
||||
RESMUST(mps_pool_create_k(&pool, arena, pool_class, args));
|
||||
} MPS_ARGS_END(args);
|
||||
watch(fn, name);
|
||||
mps_arena_park(arena);
|
||||
mps_pool_destroy(pool);
|
||||
mps_fmt_destroy(format);
|
||||
if (ngen > 0)
|
||||
mps_chain_destroy(chain);
|
||||
mps_arena_destroy(arena);
|
||||
}
|
||||
|
||||
|
||||
/* Command-line options definitions. See getopt_long(3). */
|
||||
|
||||
static struct option longopts[] = {
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{"nthreads", required_argument, NULL, 't'},
|
||||
{"niter", required_argument, NULL, 'i'},
|
||||
{"npass", required_argument, NULL, 'p'},
|
||||
{"gen", required_argument, NULL, 'g'},
|
||||
{"arena-size", required_argument, NULL, 'm'},
|
||||
{"arena-grain-size", required_argument, NULL, 'a'},
|
||||
{"width", required_argument, NULL, 'w'},
|
||||
{"depth", required_argument, NULL, 'd'},
|
||||
{"preuse", required_argument, NULL, 'r'},
|
||||
{"pupdate", required_argument, NULL, 'u'},
|
||||
{"pin-leaf", no_argument, NULL, 'l'},
|
||||
{"seed", required_argument, NULL, 'x'},
|
||||
{"arena-unzoned", no_argument, NULL, 'z'},
|
||||
{"pause-time", required_argument, NULL, 'P'},
|
||||
{"spare", required_argument, NULL, 'S'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
gcthread_fn_t fn;
|
||||
mps_pool_class_t (*pool_class)(void);
|
||||
} pools[] = {
|
||||
{"amc", gc_tree, mps_class_amc},
|
||||
{"ams", gc_tree, mps_class_ams},
|
||||
{"awl", gc_tree, mps_class_awl},
|
||||
};
|
||||
|
||||
|
||||
/* Command-line driver */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ch;
|
||||
unsigned i;
|
||||
mps_bool_t seed_specified = FALSE;
|
||||
|
||||
seed = rnd_seed();
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "ht:i:p:g:m:a:w:d:r:u:lx:zP:S:",
|
||||
longopts, NULL)) != -1)
|
||||
switch (ch) {
|
||||
case 't':
|
||||
nthreads = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'i':
|
||||
niter = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'p':
|
||||
npass = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'g':
|
||||
if (ngen >= genLIMIT) {
|
||||
fprintf(stderr, "exceeded genLIMIT\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
{
|
||||
char *p;
|
||||
size_t cap = 0;
|
||||
double mort = 0.0;
|
||||
cap = (size_t)strtoul(optarg, &p, 10);
|
||||
switch(toupper(*p)) {
|
||||
case 'G': cap <<= 20; p++; break;
|
||||
case 'M': cap <<= 10; p++; break;
|
||||
case 'K': p++; break;
|
||||
default: cap = 0; break;
|
||||
}
|
||||
if (sscanf(p, ",%lg", &mort) != 1 || cap == 0) {
|
||||
fprintf(stderr, "Bad gen format '%s'\n"
|
||||
"Each gen option has format --gen=capacity[KMG],mortality\n"
|
||||
"where capacity is a size specified in kilobytes, megabytes or gigabytes\n"
|
||||
"and mortality is a number between 0 and 1\n"
|
||||
"e.g.: --gen=500K,0.85 --gen=20M,0.45\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
gen[ngen].mps_capacity = cap;
|
||||
gen[ngen].mps_mortality = mort;
|
||||
ngen++;
|
||||
}
|
||||
break;
|
||||
case 'm': {
|
||||
char *p;
|
||||
arena_size = (unsigned)strtoul(optarg, &p, 10);
|
||||
switch(toupper(*p)) {
|
||||
case 'G': arena_size <<= 30; break;
|
||||
case 'M': arena_size <<= 20; break;
|
||||
case 'K': arena_size <<= 10; break;
|
||||
case '\0': break;
|
||||
default:
|
||||
fprintf(stderr, "Bad arena size %s\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'a': {
|
||||
char *p;
|
||||
arena_grain_size = (unsigned)strtoul(optarg, &p, 10);
|
||||
switch(toupper(*p)) {
|
||||
case 'G': arena_grain_size <<= 30; break;
|
||||
case 'M': arena_grain_size <<= 20; break;
|
||||
case 'K': arena_grain_size <<= 10; break;
|
||||
case '\0': break;
|
||||
default:
|
||||
fprintf(stderr, "Bad arena grain size %s\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
width = (size_t)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'd':
|
||||
depth = (unsigned)strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'r':
|
||||
preuse = strtod(optarg, NULL);
|
||||
break;
|
||||
case 'u':
|
||||
pupdate = strtod(optarg, NULL);
|
||||
break;
|
||||
case 'l':
|
||||
pinleaf = TRUE;
|
||||
break;
|
||||
case 'x':
|
||||
seed = strtoul(optarg, NULL, 10);
|
||||
seed_specified = TRUE;
|
||||
break;
|
||||
case 'z':
|
||||
zoned = FALSE;
|
||||
break;
|
||||
case 'P':
|
||||
pause_time = strtod(optarg, NULL);
|
||||
break;
|
||||
case 'S':
|
||||
spare = strtod(optarg, NULL);
|
||||
break;
|
||||
default:
|
||||
/* This is printed in parts to keep within the 509 character
|
||||
limit for string literals in portable standard C. */
|
||||
fprintf(stderr,
|
||||
"Usage: %s [option...] [test...]\n"
|
||||
"Options:\n"
|
||||
" -m n, --arena-size=n[KMG]?\n"
|
||||
" Initial size of arena (default %lu)\n"
|
||||
" -a n, --arena-grain-size=n[KMG]?\n"
|
||||
" Arena grain size (default %lu)\n"
|
||||
" -t n, --nthreads=n\n"
|
||||
" Launch n threads each running the test (default %u)\n"
|
||||
" -i n, --niter=n\n"
|
||||
" Iterate each test n times (default %u)\n"
|
||||
" -p n, --npass=n\n"
|
||||
" Pass over the tree n times (default %u)\n",
|
||||
argv[0],
|
||||
(unsigned long)arena_size,
|
||||
(unsigned long)arena_grain_size,
|
||||
nthreads,
|
||||
niter,
|
||||
npass);
|
||||
fprintf(stderr,
|
||||
" -g c,m, --gen=c[KMG],m\n"
|
||||
" Generation with capacity c (in Kb) and mortality m\n"
|
||||
" Use multiple times for multiple generations\n"
|
||||
" -w n, --width=n\n"
|
||||
" Width of tree nodes made (default %lu)\n"
|
||||
" -d n, --depth=n\n"
|
||||
" Depth of tree made (default %u)\n"
|
||||
" -r p, --preuse=p\n"
|
||||
" Probability of reusing a node (default %g)\n"
|
||||
" -u p, --pupdate=p\n"
|
||||
" Probability of updating a node (default %g)\n"
|
||||
" -l --pin-leaf\n"
|
||||
" Make a pinned object to use for leaves\n"
|
||||
" -x n, --seed=n\n"
|
||||
" Random number seed (default from entropy)\n",
|
||||
(unsigned long)width,
|
||||
depth,
|
||||
preuse,
|
||||
pupdate);
|
||||
fprintf(stderr,
|
||||
" -z, --arena-unzoned\n"
|
||||
" Disable zoned allocation in the arena\n"
|
||||
" -P t, --pause-time\n"
|
||||
" Maximum pause time in seconds (default %f)\n"
|
||||
" -S f, --spare\n"
|
||||
" Maximum spare committed fraction (default %f)\n"
|
||||
"Tests:\n"
|
||||
" amc pool class AMC\n"
|
||||
" ams pool class AMS\n"
|
||||
" awl pool class AWL\n",
|
||||
pause_time,
|
||||
spare);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (!seed_specified) {
|
||||
printf("seed: %lu\n", seed);
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
while (argc > 0) {
|
||||
for (i = 0; i < NELEMS(pools); ++i)
|
||||
if (strcmp(argv[0], pools[i].name) == 0)
|
||||
goto found;
|
||||
fprintf(stderr, "unknown pool test \"%s\"\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
found:
|
||||
(void)mps_lib_assert_fail_install(assert_die);
|
||||
rnd_state_set(seed);
|
||||
arena_setup(pools[i].fn, pools[i].pool_class(), pools[i].name);
|
||||
--argc;
|
||||
++argv;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2014-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
89
mps/code/getopt.h
Normal file
89
mps/code/getopt.h
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/* getopt.h -- get options from command-line argument list
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Adapted from FreeBSD getopt_long(3) to ensure cross-platform availability
|
||||
* in the Memory Pool System test programs.
|
||||
*/
|
||||
|
||||
/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
|
||||
/* $FreeBSD: src/include/getopt.h,v 1.6.30.1.8.1 2012/03/03 06:15:13 kensmith Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the NetBSD
|
||||
* Foundation, Inc. and its contributors.
|
||||
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _GETOPT_H_
|
||||
#define _GETOPT_H_
|
||||
|
||||
/*
|
||||
* GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension.
|
||||
* getopt() is declared here too for GNU programs.
|
||||
*/
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
struct option {
|
||||
/* name of long option */
|
||||
const char *name;
|
||||
/*
|
||||
* one of no_argument, required_argument, and optional_argument:
|
||||
* whether option takes an argument
|
||||
*/
|
||||
int has_arg;
|
||||
/* if not NULL, set *flag to val when option found */
|
||||
int *flag;
|
||||
/* if flag not NULL, value to set *flag to; else return value */
|
||||
int val;
|
||||
};
|
||||
|
||||
int getopt_long(int, char * const *, const char *,
|
||||
const struct option *, int *);
|
||||
int getopt_long_only(int, char * const *, const char *,
|
||||
const struct option *, int *);
|
||||
#ifndef _GETOPT_DECLARED
|
||||
#define _GETOPT_DECLARED
|
||||
int getopt(int, char * const [], const char *);
|
||||
|
||||
extern char *optarg; /* getopt(3) external variables */
|
||||
extern int optind, opterr, optopt;
|
||||
#endif
|
||||
#ifndef _OPTRESET_DECLARED
|
||||
#define _OPTRESET_DECLARED
|
||||
extern int optreset; /* getopt(3) external variable */
|
||||
#endif
|
||||
|
||||
#endif /* !_GETOPT_H_ */
|
||||
635
mps/code/getoptl.c
Normal file
635
mps/code/getoptl.c
Normal file
|
|
@ -0,0 +1,635 @@
|
|||
/* getoptl.c -- get long options from command line argument list
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
* Adapted from FreeBSD getopt_long(3) to ensure cross-platform availability
|
||||
* in the Memory Pool System test programs.
|
||||
*/
|
||||
|
||||
/* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $ */
|
||||
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the NetBSD
|
||||
* Foundation, Inc. and its contributors.
|
||||
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "getopt.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */
|
||||
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt = '?'; /* character checked for validity */
|
||||
int optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
|
||||
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||
|
||||
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||
|
||||
/* return values */
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||
#define INORDER (int)1
|
||||
|
||||
#define EMSG ""
|
||||
|
||||
#ifdef GNU_COMPATIBLE
|
||||
#define NO_PREFIX (-1)
|
||||
#define D_PREFIX 0
|
||||
#define DD_PREFIX 1
|
||||
#define W_PREFIX 2
|
||||
#endif
|
||||
|
||||
static int getopt_internal(int, char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int parse_long_options(char * const *, const char *,
|
||||
const struct option *, int *, int, int);
|
||||
static int gcd(int, int);
|
||||
static void permute_args(int, int, int, char * const *);
|
||||
|
||||
static char emsg[] = EMSG;
|
||||
static char *place = emsg; /* option letter processing */
|
||||
|
||||
/* XXX: set optreset to 1 rather than these two */
|
||||
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||
|
||||
/* Error messages */
|
||||
static const char recargchar[] = "option requires an argument -- %c";
|
||||
static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
|
||||
#ifdef GNU_COMPATIBLE
|
||||
static int dash_prefix = NO_PREFIX;
|
||||
static const char gnuoptchar[] = "invalid option -- %c";
|
||||
|
||||
static const char recargstring[] = "option `%s%s' requires an argument";
|
||||
static const char ambig[] = "option `%s%.*s' is ambiguous";
|
||||
static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
|
||||
static const char illoptstring[] = "unrecognized option `%s%s'";
|
||||
#else
|
||||
static const char recargstring[] = "option requires an argument -- %s";
|
||||
static const char ambig[] = "ambiguous option -- %.*s";
|
||||
static const char noarg[] = "option doesn't take an argument -- %.*s";
|
||||
static const char illoptstring[] = "unknown option -- %s";
|
||||
#endif
|
||||
|
||||
static void
|
||||
warnx(const char *fmt, ...)
|
||||
{
|
||||
va_list varargs;
|
||||
va_start(varargs, fmt);
|
||||
vfprintf(stderr, fmt, varargs);
|
||||
fputc('\n', stderr);
|
||||
va_end(varargs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the greatest common divisor of a and b.
|
||||
*/
|
||||
static int
|
||||
gcd(int a, int b)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = a % b;
|
||||
while (c != 0) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||
* in each block).
|
||||
*/
|
||||
static void
|
||||
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||
char * const *nargv)
|
||||
{
|
||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||
char *swap;
|
||||
|
||||
/*
|
||||
* compute lengths of blocks and number and size of cycles
|
||||
*/
|
||||
nnonopts = panonopt_end - panonopt_start;
|
||||
nopts = opt_end - panonopt_end;
|
||||
ncycle = gcd(nnonopts, nopts);
|
||||
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||
|
||||
for (i = 0; i < ncycle; i++) {
|
||||
cstart = panonopt_end+i;
|
||||
pos = cstart;
|
||||
for (j = 0; j < cyclelen; j++) {
|
||||
if (pos >= panonopt_end)
|
||||
pos -= nnonopts;
|
||||
else
|
||||
pos += nopts;
|
||||
swap = nargv[pos];
|
||||
/* LINTED const cast */
|
||||
((char **) nargv)[pos] = nargv[cstart];
|
||||
/* LINTED const cast */
|
||||
((char **)nargv)[cstart] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_long_options --
|
||||
* Parse long options in argc/argv argument vector.
|
||||
* Returns -1 if short_too is set and the option does not match long_options.
|
||||
*/
|
||||
static int
|
||||
parse_long_options(char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int short_too, int flags)
|
||||
{
|
||||
char *current_argv, *has_equal;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
const char *current_dash;
|
||||
#endif
|
||||
size_t current_argv_len;
|
||||
int i, match, exact_match, second_partial_match;
|
||||
|
||||
current_argv = place;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
switch (dash_prefix) {
|
||||
case D_PREFIX:
|
||||
current_dash = "-";
|
||||
break;
|
||||
case DD_PREFIX:
|
||||
current_dash = "--";
|
||||
break;
|
||||
case W_PREFIX:
|
||||
current_dash = "-W ";
|
||||
break;
|
||||
default:
|
||||
current_dash = "";
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
match = -1;
|
||||
exact_match = 0;
|
||||
second_partial_match = 0;
|
||||
|
||||
optind++;
|
||||
|
||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||
/* argument found (--option=arg) */
|
||||
assert(has_equal > current_argv);
|
||||
current_argv_len = (size_t)(has_equal - current_argv);
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
/* find matching long option */
|
||||
if (strncmp(current_argv, long_options[i].name,
|
||||
current_argv_len))
|
||||
continue;
|
||||
|
||||
if (strlen(long_options[i].name) == current_argv_len) {
|
||||
/* exact match */
|
||||
match = i;
|
||||
exact_match = 1;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is a known short option, don't allow
|
||||
* a partial match of a single character.
|
||||
*/
|
||||
if (short_too && current_argv_len == 1)
|
||||
continue;
|
||||
|
||||
if (match == -1) /* first partial match */
|
||||
match = i;
|
||||
else if ((flags & FLAG_LONGONLY) ||
|
||||
long_options[i].has_arg !=
|
||||
long_options[match].has_arg ||
|
||||
long_options[i].flag != long_options[match].flag ||
|
||||
long_options[i].val != long_options[match].val)
|
||||
second_partial_match = 1;
|
||||
}
|
||||
if (!exact_match && second_partial_match) {
|
||||
/* ambiguous abbreviation */
|
||||
if (PRINT_ERROR)
|
||||
fprintf(stderr,
|
||||
ambig,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
(int)current_argv_len,
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (match != -1) { /* option found */
|
||||
if (long_options[match].has_arg == no_argument
|
||||
&& has_equal) {
|
||||
if (PRINT_ERROR)
|
||||
warnx(noarg,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
(int)current_argv_len,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
return (BADCH);
|
||||
#else
|
||||
return (BADARG);
|
||||
#endif
|
||||
}
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else if (long_options[match].has_arg ==
|
||||
required_argument) {
|
||||
/*
|
||||
* optional argument doesn't use next nargv
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == NULL)) {
|
||||
/*
|
||||
* Missing argument; leading ':' indicates no error
|
||||
* should be generated.
|
||||
*/
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargstring,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
--optind;
|
||||
return (BADARG);
|
||||
}
|
||||
} else { /* unknown option */
|
||||
if (short_too) {
|
||||
--optind;
|
||||
return (-1);
|
||||
}
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptstring,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (idx)
|
||||
*idx = match;
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
return (0);
|
||||
} else
|
||||
return (long_options[match].val);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_internal --
|
||||
* Parse argc/argv argument vector. Called by user level routines.
|
||||
*/
|
||||
static int
|
||||
getopt_internal(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int flags)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
int posixly_correct; /* no static, can be changed on the fly */
|
||||
|
||||
if (options == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||
* string begins with a '+'.
|
||||
*/
|
||||
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||
#ifdef GNU_COMPATIBLE
|
||||
if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
else if (posixly_correct || *options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
#else
|
||||
if (posixly_correct || *options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
else if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
#endif
|
||||
if (*options == '+' || *options == '-')
|
||||
options++;
|
||||
|
||||
/*
|
||||
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||
* XXX using optreset. Work around this braindamage.
|
||||
*/
|
||||
if (optind == 0)
|
||||
optind = optreset = 1;
|
||||
|
||||
optarg = NULL;
|
||||
if (optreset)
|
||||
nonopt_start = nonopt_end = -1;
|
||||
start:
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc) { /* end of argument vector */
|
||||
place = emsg;
|
||||
if (nonopt_end != -1) {
|
||||
/* do permutation, if we have to */
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
else if (nonopt_start != -1) {
|
||||
/*
|
||||
* If we skipped non-options, set optind
|
||||
* to the first of them.
|
||||
*/
|
||||
optind = nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
if (*(place = nargv[optind]) != '-' ||
|
||||
#ifdef GNU_COMPATIBLE
|
||||
place[1] == '\0') {
|
||||
#else
|
||||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||
#endif
|
||||
place = emsg; /* found non-option */
|
||||
if (flags & FLAG_ALLARGS) {
|
||||
/*
|
||||
* GNU extension:
|
||||
* return non-option as argument to option 1
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
return (INORDER);
|
||||
}
|
||||
if (!(flags & FLAG_PERMUTE)) {
|
||||
/*
|
||||
* If no permutation wanted, stop parsing
|
||||
* at first non-option.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
/* do permutation */
|
||||
if (nonopt_start == -1)
|
||||
nonopt_start = optind;
|
||||
else if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
nonopt_start = optind -
|
||||
(nonopt_end - nonopt_start);
|
||||
nonopt_end = -1;
|
||||
}
|
||||
optind++;
|
||||
/* process next argument */
|
||||
goto start;
|
||||
}
|
||||
if (nonopt_start != -1 && nonopt_end == -1)
|
||||
nonopt_end = optind;
|
||||
|
||||
/*
|
||||
* If we have "-" do nothing, if "--" we are done.
|
||||
*/
|
||||
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||
optind++;
|
||||
place = emsg;
|
||||
/*
|
||||
* We found an option (--), so if we skipped
|
||||
* non-options, we have to permute.
|
||||
*/
|
||||
if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check long options if:
|
||||
* 1) we were passed some
|
||||
* 2) the arg is not just "-"
|
||||
* 3) either the arg starts with -- we are getopt_long_only()
|
||||
*/
|
||||
if (long_options != NULL && place != nargv[optind] &&
|
||||
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||
short_too = 0;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
dash_prefix = D_PREFIX;
|
||||
#endif
|
||||
if (*place == '-') {
|
||||
place++; /* --foo long option */
|
||||
#ifdef GNU_COMPATIBLE
|
||||
dash_prefix = DD_PREFIX;
|
||||
#endif
|
||||
} else if (*place != ':' && strchr(options, *place) != NULL)
|
||||
short_too = 1; /* could be short option too */
|
||||
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, short_too, flags);
|
||||
if (optchar != -1) {
|
||||
place = emsg;
|
||||
return (optchar);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optchar = (int)*place++) == (int)':' ||
|
||||
(optchar == (int)'-' && *place != '\0') ||
|
||||
(oli = strchr(options, optchar)) == NULL) {
|
||||
/*
|
||||
* If the user specified "-" and '-' isn't listed in
|
||||
* options, return -1 (non-option) as per POSIX.
|
||||
* Otherwise, it is an unknown option character (or ':').
|
||||
*/
|
||||
if (optchar == (int)'-' && *place == '\0')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
if (PRINT_ERROR)
|
||||
warnx(posixly_correct ? illoptchar : gnuoptchar,
|
||||
optchar);
|
||||
#else
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptchar, optchar);
|
||||
#endif
|
||||
optopt = optchar;
|
||||
return (BADCH);
|
||||
}
|
||||
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||
/* -W long-option */
|
||||
if (*place) /* no space */
|
||||
/* NOTHING */;
|
||||
else if (++optind >= nargc) { /* no arg */
|
||||
place = emsg;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else /* white space */
|
||||
place = nargv[optind];
|
||||
#ifdef GNU_COMPATIBLE
|
||||
dash_prefix = W_PREFIX;
|
||||
#endif
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, 0, flags);
|
||||
place = emsg;
|
||||
return (optchar);
|
||||
}
|
||||
if (*++oli != ':') { /* doesn't take argument */
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* takes (optional) argument */
|
||||
optarg = NULL;
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (oli[1] != ':') { /* arg not optional */
|
||||
if (++optind >= nargc) { /* no arg */
|
||||
place = emsg;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else
|
||||
optarg = nargv[optind];
|
||||
}
|
||||
place = emsg;
|
||||
++optind;
|
||||
}
|
||||
/* dump back option letter */
|
||||
return (optchar);
|
||||
}
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*
|
||||
* [eventually this will replace the BSD getopt]
|
||||
*/
|
||||
int
|
||||
getopt(int nargc, char * const *nargv, const char *options)
|
||||
{
|
||||
|
||||
/*
|
||||
* We don't pass FLAG_PERMUTE to getopt_internal() since
|
||||
* the BSD getopt(3) (unlike GNU) has never done this.
|
||||
*
|
||||
* Furthermore, since many privileged programs call getopt()
|
||||
* before dropping privileges it makes sense to keep things
|
||||
* as simple (and bug-free) as possible.
|
||||
*/
|
||||
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
|
||||
}
|
||||
#endif /* REPLACE_GETOPT */
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE));
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long_only --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE|FLAG_LONGONLY));
|
||||
}
|
||||
1148
mps/code/global.c
Normal file
1148
mps/code/global.c
Normal file
File diff suppressed because it is too large
Load diff
63
mps/code/gp.gmk
Normal file
63
mps/code/gp.gmk
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# gp.gmk: GNUMAKEFILE FRAGMENT FOR GNU CC/GPROF
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
#
|
||||
# This file is included by platform makefiles that use the GNU CC
|
||||
# compiler with gprof. It defines the compiler specific variables
|
||||
# that the common makefile fragment (<code/comm.gmk>) requires.
|
||||
|
||||
|
||||
CC = gcc
|
||||
CFLAGSCOMPILER = \
|
||||
-ansi -pedantic -Wall -Wextra -Werror -Wpointer-arith \
|
||||
-Wstrict-prototypes -Wmissing-prototypes \
|
||||
-Winline -Waggregate-return -Wnested-externs \
|
||||
-Wcast-qual -Wshadow -Wwrite-strings -pg
|
||||
CFLAGSDEBUG = -g -ggdb3
|
||||
CFLAGSOPT = -O -g -ggdb3
|
||||
|
||||
# gcc -MM generates a dependency line of the form:
|
||||
# thing.o : thing.c ...
|
||||
# The sed line converts this into:
|
||||
# <pfm>/<variety>/thing.o <pfm>/<variety>/thing.d : thing.c ...
|
||||
# @@ This sequence is vulnerable to interrupts (for some reason)
|
||||
|
||||
define gendep
|
||||
$(SHELL) -ec "gcc -c $(CFLAGS) -MM $< | \
|
||||
sed '/:/s!$*.o!$(@D)/& $(@D)/$*.d!' > $@"
|
||||
endef
|
||||
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
646
mps/code/land.c
Normal file
646
mps/code/land.c
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
/* land.c: LAND (COLLECTION OF ADDRESS RANGES) IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2014-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .design: <design/land>
|
||||
*
|
||||
* .critical.macros: In manual-allocation-bound programs using MVFF,
|
||||
* the Land generic functions are on the critical path via mps_free.
|
||||
* In non-checking varieties we provide macro alternatives (in mpm.h)
|
||||
* to these functions that call the underlying methods directly,
|
||||
* giving a few percent improvement in performance but skipping the
|
||||
* re-entrancy checking provided by landEnter and landLeave.
|
||||
*/
|
||||
|
||||
#include "mpm.h"
|
||||
#include "range.h"
|
||||
|
||||
SRCID(land, "$Id$");
|
||||
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
static Res landNoInsert(Range rangeReturn, Land land, Range range);
|
||||
static Res landNoDelete(Range rangeReturn, Land land, Range range);
|
||||
|
||||
|
||||
/* FindDeleteCheck -- check method for a FindDelete value */
|
||||
|
||||
Bool FindDeleteCheck(FindDelete findDelete)
|
||||
{
|
||||
CHECKL(findDelete == FindDeleteNONE
|
||||
|| findDelete == FindDeleteLOW
|
||||
|| findDelete == FindDeleteHIGH
|
||||
|| findDelete == FindDeleteENTIRE);
|
||||
UNUSED(findDelete); /* <code/mpm.c#check.unused> */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* landEnter, landLeave -- Avoid re-entrance
|
||||
*
|
||||
* .enter-leave: The visitor functions passed to LandIterate and
|
||||
* LandIterateAndDelete are not allowed to call methods of that land.
|
||||
* These functions enforce this.
|
||||
*
|
||||
* .enter-leave.simple: Some simple queries are fine to call from
|
||||
* visitor functions. These are marked with the tag of this comment.
|
||||
*/
|
||||
|
||||
static void landEnter(Land land)
|
||||
{
|
||||
/* Don't need to check as always called from interface function. */
|
||||
AVER(!land->inLand);
|
||||
land->inLand = TRUE;
|
||||
}
|
||||
|
||||
static void landLeave(Land land)
|
||||
{
|
||||
/* Don't need to check as always called from interface function. */
|
||||
AVER(land->inLand);
|
||||
land->inLand = FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* LandCheck -- check land */
|
||||
|
||||
Bool LandCheck(Land land)
|
||||
{
|
||||
LandClass klass;
|
||||
/* .enter-leave.simple */
|
||||
CHECKS(Land, land);
|
||||
CHECKC(Land, land);
|
||||
klass = ClassOfPoly(Land, land);
|
||||
CHECKD(LandClass, klass);
|
||||
CHECKU(Arena, land->arena);
|
||||
CHECKL(AlignCheck(land->alignment));
|
||||
CHECKL(BoolCheck(land->inLand));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static Res LandAbsInit(Land land, Arena arena, Align alignment, ArgList args)
|
||||
{
|
||||
AVER(land != NULL);
|
||||
AVERT(Arena, arena);
|
||||
AVERT(Align, alignment);
|
||||
UNUSED(args);
|
||||
|
||||
/* Superclass init */
|
||||
InstInit(CouldBeA(Inst, land));
|
||||
|
||||
land->inLand = TRUE;
|
||||
land->alignment = alignment;
|
||||
land->arena = arena;
|
||||
|
||||
SetClassOfPoly(land, CLASS(Land));
|
||||
land->sig = LandSig;
|
||||
AVERC(Land, land);
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
static void LandAbsFinish(Inst inst)
|
||||
{
|
||||
Land land = MustBeA(Land, inst);
|
||||
AVERC(Land, land);
|
||||
land->sig = SigInvalid;
|
||||
NextMethod(Inst, Land, finish)(inst);
|
||||
}
|
||||
|
||||
|
||||
/* LandInit -- initialize land
|
||||
*
|
||||
* <design/land#.function.init>
|
||||
*/
|
||||
|
||||
Res LandInit(Land land, LandClass klass, Arena arena, Align alignment, void *owner, ArgList args)
|
||||
{
|
||||
Res res;
|
||||
|
||||
AVER(land != NULL);
|
||||
AVERT(LandClass, klass);
|
||||
AVERT(Align, alignment);
|
||||
|
||||
res = klass->init(land, arena, alignment, args);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
EVENT2(LandInit, land, owner);
|
||||
landLeave(land);
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* LandFinish -- finish land
|
||||
*
|
||||
* <design/land#.function.finish>
|
||||
*/
|
||||
|
||||
void LandFinish(Land land)
|
||||
{
|
||||
AVERC(Land, land);
|
||||
landEnter(land);
|
||||
|
||||
Method(Inst, land, finish)(MustBeA(Inst, land));
|
||||
}
|
||||
|
||||
|
||||
/* LandSize -- return the total size of ranges in land
|
||||
*
|
||||
* <design/land#.function.size>
|
||||
*/
|
||||
|
||||
Size (LandSize)(Land land)
|
||||
{
|
||||
/* .enter-leave.simple */
|
||||
AVERC(Land, land);
|
||||
|
||||
return LandSizeMacro(land);
|
||||
}
|
||||
|
||||
|
||||
/* LandInsert -- insert range of addresses into land
|
||||
*
|
||||
* <design/land#.function.insert>
|
||||
*/
|
||||
|
||||
Res (LandInsert)(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Res res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVERT(Range, range);
|
||||
AVER(RangeIsAligned(range, land->alignment));
|
||||
AVER(!RangeIsEmpty(range));
|
||||
landEnter(land);
|
||||
|
||||
res = LandInsertMacro(rangeReturn, land, range);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* LandInsertSteal -- insert range of addresses into land, possibly
|
||||
* stealing some of the inserted memory to allocate internal data
|
||||
* structures.
|
||||
*
|
||||
* <design/land#.function.insert-steal>
|
||||
*/
|
||||
|
||||
Res LandInsertSteal(Range rangeReturn, Land land, Range rangeIO)
|
||||
{
|
||||
Res res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVER(rangeReturn != rangeIO);
|
||||
AVERT(Range, rangeIO);
|
||||
AVER(RangeIsAligned(rangeIO, land->alignment));
|
||||
AVER(!RangeIsEmpty(rangeIO));
|
||||
|
||||
landEnter(land);
|
||||
|
||||
res = Method(Land, land, insertSteal)(rangeReturn, land, rangeIO);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* LandDelete -- delete range of addresses from land
|
||||
*
|
||||
* <design/land#.function.delete>
|
||||
*/
|
||||
|
||||
Res (LandDelete)(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Res res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVERT(Range, range);
|
||||
AVER(!RangeIsEmpty(range));
|
||||
AVER(RangeIsAligned(range, land->alignment));
|
||||
landEnter(land);
|
||||
|
||||
res = LandDeleteMacro(rangeReturn, land, range);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* LandDeleteSteal -- delete range of addresses from land, possibly
|
||||
* stealing some memory from the land to allocate internal data
|
||||
* structures.
|
||||
*
|
||||
* <design/land#.function.delete-steal>
|
||||
*/
|
||||
|
||||
Res LandDeleteSteal(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
Res res;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVERT(Range, range);
|
||||
AVER(!RangeIsEmpty(range));
|
||||
AVER(RangeIsAligned(range, land->alignment));
|
||||
landEnter(land);
|
||||
|
||||
res = Method(Land, land, deleteSteal)(rangeReturn, land, range);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* LandIterate -- iterate over isolated ranges of addresses in land
|
||||
*
|
||||
* <design/land#.function.iterate>
|
||||
*/
|
||||
|
||||
Bool (LandIterate)(Land land, LandVisitor visitor, void *closure)
|
||||
{
|
||||
Bool b;
|
||||
AVERC(Land, land);
|
||||
AVER(FUNCHECK(visitor));
|
||||
landEnter(land);
|
||||
|
||||
b = LandIterateMacro(land, visitor, closure);
|
||||
|
||||
landLeave(land);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/* LandIterateAndDelete -- iterate over isolated ranges of addresses
|
||||
* in land, deleting some of them
|
||||
*
|
||||
* <design/land#.function.iterate.and.delete>
|
||||
*/
|
||||
|
||||
Bool (LandIterateAndDelete)(Land land, LandDeleteVisitor visitor, void *closure)
|
||||
{
|
||||
Bool b;
|
||||
AVERC(Land, land);
|
||||
AVER(FUNCHECK(visitor));
|
||||
landEnter(land);
|
||||
|
||||
b = LandIterateAndDeleteMacro(land, visitor, closure);
|
||||
|
||||
landLeave(land);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/* LandFindFirst -- find first range of given size
|
||||
*
|
||||
* <design/land#.function.find.first>
|
||||
*/
|
||||
|
||||
Bool (LandFindFirst)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Bool b;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
AVERT(FindDelete, findDelete);
|
||||
landEnter(land);
|
||||
|
||||
b = LandFindFirstMacro(rangeReturn, oldRangeReturn, land, size, findDelete);
|
||||
|
||||
landLeave(land);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/* LandFindLast -- find last range of given size
|
||||
*
|
||||
* <design/land#.function.find.last>
|
||||
*/
|
||||
|
||||
Bool (LandFindLast)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Bool b;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
AVERT(FindDelete, findDelete);
|
||||
landEnter(land);
|
||||
|
||||
b = LandFindLastMacro(rangeReturn, oldRangeReturn, land, size, findDelete);
|
||||
|
||||
landLeave(land);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/* LandFindLargest -- find largest range of at least given size
|
||||
*
|
||||
* <design/land#.function.find.largest>
|
||||
*/
|
||||
|
||||
Bool (LandFindLargest)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
Bool b;
|
||||
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
AVERT(FindDelete, findDelete);
|
||||
landEnter(land);
|
||||
|
||||
b = LandFindLargestMacro(rangeReturn, oldRangeReturn, land, size, findDelete);
|
||||
|
||||
landLeave(land);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
/* LandFindInSize -- find range of given size in set of zones
|
||||
*
|
||||
* <design/land#.function.find.zones>
|
||||
*/
|
||||
|
||||
Res (LandFindInZones)(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)
|
||||
{
|
||||
Res res;
|
||||
|
||||
AVER(foundReturn != NULL);
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVER(SizeIsAligned(size, land->alignment));
|
||||
/* AVER(ZoneSet, zoneSet); */
|
||||
AVERT(Bool, high);
|
||||
landEnter(land);
|
||||
|
||||
res = LandFindInZonesMacro(foundReturn, rangeReturn, oldRangeReturn,
|
||||
land, size, zoneSet, high);
|
||||
|
||||
landLeave(land);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* LandDescribe -- describe land for debugging
|
||||
*
|
||||
* <design/land#.function.describe>
|
||||
*/
|
||||
|
||||
Res LandDescribe(Land land, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
return Method(Inst, land, describe)(MustBeA(Inst, land), stream, depth);
|
||||
}
|
||||
|
||||
|
||||
/* landFlushVisitor -- visitor for LandFlush.
|
||||
*
|
||||
* closure argument is the destination Land. Attempt to insert the
|
||||
* range into the destination.
|
||||
*
|
||||
* .flush.critical: In manual-allocation-bound programs using MVFF
|
||||
* this is on the critical paths via mps_alloc (and then PoolAlloc,
|
||||
* MVFFAlloc, failoverFind*, LandFlush) and mps_free (and then
|
||||
* MVFFFree, failoverInsert, LandFlush).
|
||||
*/
|
||||
Bool LandFlushVisitor(Bool *deleteReturn, Land land, Range range,
|
||||
void *closure)
|
||||
{
|
||||
Res res;
|
||||
RangeStruct newRange;
|
||||
Land dest;
|
||||
|
||||
AVER_CRITICAL(deleteReturn != NULL);
|
||||
AVERC_CRITICAL(Land, land);
|
||||
AVERT_CRITICAL(Range, range);
|
||||
AVER_CRITICAL(closure != NULL);
|
||||
|
||||
dest = MustBeA_CRITICAL(Land, closure);
|
||||
res = LandInsert(&newRange, dest, range);
|
||||
if (res == ResOK) {
|
||||
*deleteReturn = TRUE;
|
||||
return TRUE;
|
||||
} else {
|
||||
*deleteReturn = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* LandFlush -- move ranges from src to dest
|
||||
*
|
||||
* <design/land#.function.flush>
|
||||
*/
|
||||
|
||||
Bool (LandFlush)(Land dest, Land src)
|
||||
{
|
||||
AVERC(Land, dest);
|
||||
AVERC(Land, src);
|
||||
|
||||
return LandFlushMacro(dest, src);
|
||||
}
|
||||
|
||||
|
||||
/* LandClassCheck -- check land class */
|
||||
|
||||
Bool LandClassCheck(LandClass klass)
|
||||
{
|
||||
CHECKL(InstClassCheck(&klass->instClassStruct));
|
||||
CHECKL(klass->size >= sizeof(LandStruct));
|
||||
CHECKL(FUNCHECK(klass->init));
|
||||
CHECKL(FUNCHECK(klass->insert));
|
||||
CHECKL(FUNCHECK(klass->delete));
|
||||
CHECKL(FUNCHECK(klass->findFirst));
|
||||
CHECKL(FUNCHECK(klass->findLast));
|
||||
CHECKL(FUNCHECK(klass->findLargest));
|
||||
CHECKL(FUNCHECK(klass->findInZones));
|
||||
|
||||
/* Check that land classes override sets of related methods. */
|
||||
CHECKL((klass->init == LandAbsInit)
|
||||
== (klass->instClassStruct.finish == LandAbsFinish));
|
||||
CHECKL((klass->insert == landNoInsert) == (klass->delete == landNoDelete));
|
||||
|
||||
CHECKS(LandClass, klass);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static Size landNoSize(Land land)
|
||||
{
|
||||
UNUSED(land);
|
||||
NOTREACHED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* LandSlowSize -- generic size method but slow */
|
||||
|
||||
static Bool landSizeVisitor(Land land, Range range,
|
||||
void *closure)
|
||||
{
|
||||
Size *size;
|
||||
|
||||
AVERC(Land, land);
|
||||
AVERT(Range, range);
|
||||
AVER(closure != NULL);
|
||||
|
||||
size = closure;
|
||||
*size += RangeSize(range);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Size LandSlowSize(Land land)
|
||||
{
|
||||
Size size = 0;
|
||||
Bool b = LandIterate(land, landSizeVisitor, &size);
|
||||
AVER(b);
|
||||
return size;
|
||||
}
|
||||
|
||||
static Res landNoInsert(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVERT(Range, range);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static Res landNoDelete(Range rangeReturn, Land land, Range range)
|
||||
{
|
||||
AVER(rangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
AVERT(Range, range);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static Bool landNoIterate(Land land, LandVisitor visitor, void *closure)
|
||||
{
|
||||
AVERC(Land, land);
|
||||
AVER(visitor != NULL);
|
||||
UNUSED(closure);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static Bool landNoIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closure)
|
||||
{
|
||||
AVERC(Land, land);
|
||||
AVER(visitor != NULL);
|
||||
UNUSED(closure);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete)
|
||||
{
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
UNUSED(size);
|
||||
AVERT(FindDelete, findDelete);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static Res landNoFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high)
|
||||
{
|
||||
AVER(foundReturn != NULL);
|
||||
AVER(rangeReturn != NULL);
|
||||
AVER(oldRangeReturn != NULL);
|
||||
AVERC(Land, land);
|
||||
UNUSED(size);
|
||||
UNUSED(zoneSet);
|
||||
AVERT(Bool, high);
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
static Res LandAbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
Land land = CouldBeA(Land, inst);
|
||||
LandClass klass;
|
||||
Res res;
|
||||
|
||||
if (!TESTC(Land, land))
|
||||
return ResPARAM;
|
||||
if (stream == NULL)
|
||||
return ResPARAM;
|
||||
|
||||
res = NextMethod(Inst, Land, describe)(inst, stream, depth);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
klass = ClassOfPoly(Land, land);
|
||||
return WriteF(stream, depth + 2,
|
||||
"class $P (\"$S\")\n",
|
||||
(WriteFP)klass, (WriteFS)ClassName(klass),
|
||||
"arena $P\n", (WriteFP)land->arena,
|
||||
"align $U\n", (WriteFU)land->alignment,
|
||||
"inLand $S\n", WriteFYesNo(land->inLand),
|
||||
NULL);
|
||||
}
|
||||
|
||||
DEFINE_CLASS(Inst, LandClass, klass)
|
||||
{
|
||||
INHERIT_CLASS(klass, LandClass, InstClass);
|
||||
AVERT(InstClass, klass);
|
||||
}
|
||||
|
||||
DEFINE_CLASS(Land, Land, klass)
|
||||
{
|
||||
INHERIT_CLASS(&klass->instClassStruct, Land, Inst);
|
||||
klass->instClassStruct.describe = LandAbsDescribe;
|
||||
klass->instClassStruct.finish = LandAbsFinish;
|
||||
klass->size = sizeof(LandStruct);
|
||||
klass->init = LandAbsInit;
|
||||
klass->sizeMethod = landNoSize;
|
||||
klass->insert = landNoInsert;
|
||||
klass->insertSteal = landNoInsert;
|
||||
klass->delete = landNoDelete;
|
||||
klass->deleteSteal = landNoDelete;
|
||||
klass->iterate = landNoIterate;
|
||||
klass->iterateAndDelete = landNoIterateAndDelete;
|
||||
klass->findFirst = landNoFind;
|
||||
klass->findLast = landNoFind;
|
||||
klass->findLargest = landNoFind;
|
||||
klass->findInZones = landNoFindInZones;
|
||||
klass->sig = LandClassSig;
|
||||
AVERT(LandClass, klass);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2014-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
680
mps/code/landtest.c
Normal file
680
mps/code/landtest.c
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
/* landtest.c: LAND TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* Test all three Land implementations against duplicate operations on
|
||||
* a bit-table.
|
||||
*
|
||||
* Test the "steal" operations on a CBS.
|
||||
*/
|
||||
|
||||
#include "cbs.h"
|
||||
#include "failover.h"
|
||||
#include "freelist.h"
|
||||
#include "mpm.h"
|
||||
#include "mps.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpstd.h"
|
||||
#include "poolmfs.h"
|
||||
#include "testlib.h"
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
|
||||
SRCID(landtest, "$Id$");
|
||||
|
||||
|
||||
#define ArraySize ((Size)123456)
|
||||
|
||||
/* CBS is much faster than Freelist, so we apply more operations to
|
||||
* the former. */
|
||||
#define nCBSOperations ((Size)125000)
|
||||
#define nFLOperations ((Size)12500)
|
||||
#define nFOOperations ((Size)12500)
|
||||
|
||||
static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried,
|
||||
NDeallocateSucceeded;
|
||||
|
||||
static int verbose = 0;
|
||||
|
||||
typedef struct TestStateStruct {
|
||||
Align align;
|
||||
BT allocTable;
|
||||
Addr block;
|
||||
Size size;
|
||||
Land land;
|
||||
} TestStateStruct, *TestState;
|
||||
|
||||
typedef struct CheckTestClosureStruct {
|
||||
TestState state;
|
||||
Addr limit;
|
||||
Addr oldLimit;
|
||||
} CheckTestClosureStruct, *CheckTestClosure;
|
||||
|
||||
|
||||
static Addr (addrOfIndex)(TestState state, Index i)
|
||||
{
|
||||
return AddrAdd(state->block, (i * state->align));
|
||||
}
|
||||
|
||||
|
||||
static Index (indexOfAddr)(TestState state, Addr a)
|
||||
{
|
||||
return (Index)(AddrOffset(state->block, a) / state->align);
|
||||
}
|
||||
|
||||
|
||||
static void describe(TestState state)
|
||||
{
|
||||
die(LandDescribe(state->land, mps_lib_get_stdout(), 0), "LandDescribe");
|
||||
}
|
||||
|
||||
|
||||
static Bool checkVisitor(Land land, Range range, void *closure)
|
||||
{
|
||||
Addr base, limit;
|
||||
CheckTestClosure cl = closure;
|
||||
|
||||
testlib_unused(land);
|
||||
Insist(cl != NULL);
|
||||
|
||||
base = RangeBase(range);
|
||||
limit = RangeLimit(range);
|
||||
|
||||
if (base > cl->oldLimit) {
|
||||
Insist(BTIsSetRange(cl->state->allocTable,
|
||||
indexOfAddr(cl->state, cl->oldLimit),
|
||||
indexOfAddr(cl->state, base)));
|
||||
} else { /* must be at start of table */
|
||||
Insist(base == cl->oldLimit);
|
||||
Insist(cl->oldLimit == cl->state->block);
|
||||
}
|
||||
|
||||
Insist(BTIsResRange(cl->state->allocTable,
|
||||
indexOfAddr(cl->state, base),
|
||||
indexOfAddr(cl->state, limit)));
|
||||
|
||||
cl->oldLimit = limit;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void check(TestState state)
|
||||
{
|
||||
CheckTestClosureStruct closure;
|
||||
Bool b;
|
||||
|
||||
closure.state = state;
|
||||
closure.limit = addrOfIndex(state, state->size);
|
||||
closure.oldLimit = state->block;
|
||||
|
||||
b = LandIterate(state->land, checkVisitor, &closure);
|
||||
Insist(b);
|
||||
|
||||
if (closure.oldLimit == state->block)
|
||||
Insist(BTIsSetRange(state->allocTable, 0,
|
||||
indexOfAddr(state, closure.limit)));
|
||||
else if (closure.limit > closure.oldLimit)
|
||||
Insist(BTIsSetRange(state->allocTable,
|
||||
indexOfAddr(state, closure.oldLimit),
|
||||
indexOfAddr(state, closure.limit)));
|
||||
else
|
||||
Insist(closure.oldLimit == closure.limit);
|
||||
}
|
||||
|
||||
|
||||
static Word fbmRnd(Word limit)
|
||||
{
|
||||
/* Not very uniform, but never mind. */
|
||||
return (Word)rnd() % limit;
|
||||
}
|
||||
|
||||
|
||||
/* nextEdge -- Finds the next transition in the bit table
|
||||
*
|
||||
* Returns the index greater than <base> such that the
|
||||
* range [<base>, <return>) has the same value in the bit table,
|
||||
* and <return> has a different value or does not exist.
|
||||
*/
|
||||
|
||||
static Index nextEdge(BT bt, Size size, Index base)
|
||||
{
|
||||
Index end;
|
||||
Bool baseValue;
|
||||
|
||||
Insist(bt != NULL);
|
||||
Insist(base < size);
|
||||
|
||||
baseValue = BTGet(bt, base);
|
||||
|
||||
for(end = base + 1; end < size && BTGet(bt, end) == baseValue; end++)
|
||||
NOOP;
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
/* lastEdge -- Finds the previous transition in the bit table
|
||||
*
|
||||
* Returns the index less than <base> such that the range
|
||||
* [<return>, <base>] has the same value in the bit table,
|
||||
* and <return>-1 has a different value or does not exist.
|
||||
*/
|
||||
|
||||
static Index lastEdge(BT bt, Size size, Index base)
|
||||
{
|
||||
Index end;
|
||||
Bool baseValue;
|
||||
|
||||
Insist(bt != NULL);
|
||||
Insist(base < size);
|
||||
|
||||
baseValue = BTGet(bt, base);
|
||||
|
||||
for(end = base; end > (Index)0 && BTGet(bt, end - 1) == baseValue; end--)
|
||||
NOOP;
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
/* randomRange -- picks random range within table
|
||||
*
|
||||
* The function first picks a uniformly distributed <base> within the table.
|
||||
*
|
||||
* It then scans forward a binary exponentially distributed number of
|
||||
* "edges" in the table (that is, transitions between set and reset)
|
||||
* to get <end>. Note that there is a 50% chance that <end> will be
|
||||
* the next edge, a 25% chance it will be the edge after, etc., until
|
||||
* the end of the table.
|
||||
*
|
||||
* Finally it picks a <limit> uniformly distributed in the range
|
||||
* [base+1, limit].
|
||||
*
|
||||
* Hence there is a somewhat better than 50% chance that the range will be
|
||||
* all either set or reset.
|
||||
*/
|
||||
|
||||
static void randomRange(Addr *baseReturn, Addr *limitReturn, TestState state)
|
||||
{
|
||||
Index base; /* the start of our range */
|
||||
Index end; /* an edge (i.e. different from its predecessor) */
|
||||
/* after base */
|
||||
Index limit; /* a randomly chosen value in (base, limit]. */
|
||||
|
||||
base = fbmRnd(state->size);
|
||||
|
||||
do {
|
||||
end = nextEdge(state->allocTable, state->size, base);
|
||||
} while (end < state->size && fbmRnd(2) == 0); /* p=0.5 exponential */
|
||||
|
||||
Insist(end > base);
|
||||
|
||||
limit = base + 1 + fbmRnd(end - base);
|
||||
|
||||
*baseReturn = addrOfIndex(state, base);
|
||||
*limitReturn = addrOfIndex(state, limit);
|
||||
}
|
||||
|
||||
|
||||
static void allocate(TestState state, Addr base, Addr limit)
|
||||
{
|
||||
Res res;
|
||||
Index ib, il; /* Indexed for base and limit */
|
||||
Bool isFree;
|
||||
RangeStruct range, oldRange;
|
||||
Addr outerBase, outerLimit; /* interval containing [ib, il) */
|
||||
|
||||
ib = indexOfAddr(state, base);
|
||||
il = indexOfAddr(state, limit);
|
||||
|
||||
isFree = BTIsResRange(state->allocTable, ib, il);
|
||||
|
||||
NAllocateTried++;
|
||||
|
||||
if (isFree) {
|
||||
outerBase =
|
||||
addrOfIndex(state, lastEdge(state->allocTable, state->size, ib));
|
||||
outerLimit =
|
||||
addrOfIndex(state, nextEdge(state->allocTable, state->size, il - 1));
|
||||
} else {
|
||||
outerBase = outerLimit = NULL;
|
||||
}
|
||||
|
||||
RangeInit(&range, base, limit);
|
||||
res = LandDelete(&oldRange, state->land, &range);
|
||||
|
||||
if (verbose) {
|
||||
printf("allocate: [%p,%p) -- %s\n",
|
||||
(void *)base, (void *)limit, isFree ? "succeed" : "fail");
|
||||
describe(state);
|
||||
}
|
||||
|
||||
if (!isFree) {
|
||||
die_expect((mps_res_t)res, MPS_RES_FAIL,
|
||||
"Succeeded in deleting allocated block");
|
||||
} else { /* isFree */
|
||||
die_expect((mps_res_t)res, MPS_RES_OK,
|
||||
"failed to delete free block");
|
||||
Insist(RangeBase(&oldRange) == outerBase);
|
||||
Insist(RangeLimit(&oldRange) == outerLimit);
|
||||
NAllocateSucceeded++;
|
||||
BTSetRange(state->allocTable, ib, il);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void deallocate(TestState state, Addr base, Addr limit)
|
||||
{
|
||||
Res res;
|
||||
Index ib, il;
|
||||
Bool isAllocated;
|
||||
Addr outerBase = base, outerLimit = limit; /* interval containing [ib, il) */
|
||||
RangeStruct range, freeRange; /* interval returned by the manager */
|
||||
|
||||
ib = indexOfAddr(state, base);
|
||||
il = indexOfAddr(state, limit);
|
||||
|
||||
isAllocated = BTIsSetRange(state->allocTable, ib, il);
|
||||
|
||||
NDeallocateTried++;
|
||||
|
||||
if (isAllocated) {
|
||||
/* Find the free blocks adjacent to the allocated block */
|
||||
if (ib > 0 && !BTGet(state->allocTable, ib - 1)) {
|
||||
outerBase =
|
||||
addrOfIndex(state, lastEdge(state->allocTable, state->size, ib - 1));
|
||||
} else {
|
||||
outerBase = base;
|
||||
}
|
||||
|
||||
if (il < state->size && !BTGet(state->allocTable, il)) {
|
||||
outerLimit =
|
||||
addrOfIndex(state, nextEdge(state->allocTable, state->size, il));
|
||||
} else {
|
||||
outerLimit = limit;
|
||||
}
|
||||
}
|
||||
|
||||
RangeInit(&range, base, limit);
|
||||
res = LandInsert(&freeRange, state->land, &range);
|
||||
|
||||
if (verbose) {
|
||||
printf("deallocate: [%p,%p) -- %s\n",
|
||||
(void *)base, (void *)limit, isAllocated ? "succeed" : "fail");
|
||||
describe(state);
|
||||
}
|
||||
|
||||
if (!isAllocated) {
|
||||
die_expect((mps_res_t)res, MPS_RES_FAIL,
|
||||
"succeeded in inserting non-allocated block");
|
||||
} else { /* isAllocated */
|
||||
die_expect((mps_res_t)res, MPS_RES_OK,
|
||||
"failed to insert allocated block");
|
||||
|
||||
NDeallocateSucceeded++;
|
||||
BTResRange(state->allocTable, ib, il);
|
||||
Insist(RangeBase(&freeRange) == outerBase);
|
||||
Insist(RangeLimit(&freeRange) == outerLimit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void find(TestState state, Size size, Bool high, FindDelete findDelete)
|
||||
{
|
||||
Bool expected, found;
|
||||
Index expectedBase, expectedLimit;
|
||||
RangeStruct foundRange, oldRange;
|
||||
Addr origBase, origLimit;
|
||||
|
||||
origBase = origLimit = NULL;
|
||||
expected = (high ? BTFindLongResRangeHigh : BTFindLongResRange)
|
||||
(&expectedBase, &expectedLimit, state->allocTable,
|
||||
(Index)0, (Index)state->size, (Count)size);
|
||||
|
||||
if (expected) {
|
||||
origBase = addrOfIndex(state, expectedBase);
|
||||
origLimit = addrOfIndex(state, expectedLimit);
|
||||
|
||||
switch(findDelete) {
|
||||
case FindDeleteNONE:
|
||||
/* do nothing */
|
||||
break;
|
||||
case FindDeleteENTIRE:
|
||||
break;
|
||||
case FindDeleteLOW:
|
||||
expectedLimit = expectedBase + size;
|
||||
break;
|
||||
case FindDeleteHIGH:
|
||||
expectedBase = expectedLimit - size;
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid findDelete");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
found = (high ? LandFindLast : LandFindFirst)
|
||||
(&foundRange, &oldRange, state->land, size * state->align, findDelete);
|
||||
|
||||
if (verbose) {
|
||||
printf("find %s %lu: ", high ? "last" : "first",
|
||||
(unsigned long)(size * state->align));
|
||||
if (expected) {
|
||||
printf("expecting [%p,%p)\n",
|
||||
(void *)addrOfIndex(state, expectedBase),
|
||||
(void *)addrOfIndex(state, expectedLimit));
|
||||
} else {
|
||||
printf("expecting this not to be found\n");
|
||||
}
|
||||
if (found) {
|
||||
printf(" found [%p,%p)\n", (void *)RangeBase(&foundRange),
|
||||
(void *)RangeLimit(&foundRange));
|
||||
} else {
|
||||
printf(" not found\n");
|
||||
}
|
||||
}
|
||||
|
||||
Insist(found == expected);
|
||||
|
||||
if (found) {
|
||||
Insist(expectedBase == indexOfAddr(state, RangeBase(&foundRange)));
|
||||
Insist(expectedLimit == indexOfAddr(state, RangeLimit(&foundRange)));
|
||||
|
||||
if (findDelete != FindDeleteNONE) {
|
||||
Insist(RangeBase(&oldRange) == origBase);
|
||||
Insist(RangeLimit(&oldRange) == origLimit);
|
||||
BTSetRange(state->allocTable, expectedBase, expectedLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test(TestState state, unsigned n, unsigned operations)
|
||||
{
|
||||
Addr base, limit;
|
||||
unsigned i;
|
||||
Size size;
|
||||
Bool high;
|
||||
FindDelete findDelete = FindDeleteNONE;
|
||||
|
||||
BTSetRange(state->allocTable, 0, state->size); /* Initially all allocated */
|
||||
check(state);
|
||||
for(i = 0; i < n; i++) {
|
||||
switch (fbmRnd(operations)) {
|
||||
case 0:
|
||||
randomRange(&base, &limit, state);
|
||||
allocate(state, base, limit);
|
||||
break;
|
||||
case 1:
|
||||
randomRange(&base, &limit, state);
|
||||
deallocate(state, base, limit);
|
||||
break;
|
||||
case 2:
|
||||
size = fbmRnd(state->size / 10) + 1;
|
||||
high = fbmRnd(2) ? TRUE : FALSE;
|
||||
switch(fbmRnd(6)) {
|
||||
default: findDelete = FindDeleteNONE; break;
|
||||
case 3: findDelete = FindDeleteLOW; break;
|
||||
case 4: findDelete = FindDeleteHIGH; break;
|
||||
case 5: findDelete = FindDeleteENTIRE; break;
|
||||
}
|
||||
find(state, size, high, findDelete);
|
||||
break;
|
||||
default:
|
||||
cdie(0, "invalid operation");
|
||||
return;
|
||||
}
|
||||
if ((i + 1) % 1000 == 0)
|
||||
check(state);
|
||||
}
|
||||
}
|
||||
|
||||
#define testArenaSIZE (((size_t)4)<<20)
|
||||
|
||||
static void test_land(void)
|
||||
{
|
||||
static const struct {
|
||||
LandClass (*klass)(void);
|
||||
unsigned operations;
|
||||
} cbsConfig[] = {
|
||||
{CBSClassGet, 2},
|
||||
{CBSFastClassGet, 3},
|
||||
{CBSZonedClassGet, 3},
|
||||
};
|
||||
mps_arena_t mpsArena;
|
||||
Arena arena;
|
||||
TestStateStruct state;
|
||||
void *p;
|
||||
MFSStruct blockPool;
|
||||
CBSStruct cbsStruct;
|
||||
FreelistStruct flStruct;
|
||||
FailoverStruct foStruct;
|
||||
Land cbs = CBSLand(&cbsStruct);
|
||||
Land fl = FreelistLand(&flStruct);
|
||||
Land fo = FailoverLand(&foStruct);
|
||||
Pool mfs = MFSPool(&blockPool);
|
||||
size_t i;
|
||||
|
||||
state.size = ArraySize;
|
||||
state.align = (1 << rnd() % 4) * MPS_PF_ALIGN;
|
||||
|
||||
NAllocateTried = NAllocateSucceeded = NDeallocateTried =
|
||||
NDeallocateSucceeded = 0;
|
||||
|
||||
die(mps_arena_create(&mpsArena, mps_arena_class_vm(), testArenaSIZE),
|
||||
"mps_arena_create");
|
||||
arena = (Arena)mpsArena; /* avoid pun */
|
||||
|
||||
die((mps_res_t)BTCreate(&state.allocTable, arena, state.size),
|
||||
"failed to create alloc table");
|
||||
|
||||
die((mps_res_t)ControlAlloc(&p, arena, (state.size + 1) * state.align),
|
||||
"failed to allocate block");
|
||||
state.block = AddrAlignUp(p, state.align);
|
||||
|
||||
if (verbose) {
|
||||
printf("Allocated block [%p,%p)\n", (void *)state.block,
|
||||
(void *)AddrAdd(state.block, state.size));
|
||||
}
|
||||
|
||||
/* 1. Test CBS */
|
||||
|
||||
for (i = 0; i < NELEMS(cbsConfig); ++i) {
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
die((mps_res_t)LandInit(cbs, cbsConfig[i].klass(), arena, state.align,
|
||||
NULL, args),
|
||||
"failed to initialise CBS");
|
||||
} MPS_ARGS_END(args);
|
||||
state.land = cbs;
|
||||
test(&state, nCBSOperations, cbsConfig[i].operations);
|
||||
LandFinish(cbs);
|
||||
}
|
||||
|
||||
/* 2. Test Freelist */
|
||||
|
||||
die((mps_res_t)LandInit(fl, CLASS(Freelist), arena, state.align,
|
||||
NULL, mps_args_none),
|
||||
"failed to initialise Freelist");
|
||||
state.land = fl;
|
||||
test(&state, nFLOperations, 3);
|
||||
LandFinish(fl);
|
||||
|
||||
/* 3. Test CBS-failing-over-to-Freelist (always failing over on
|
||||
* first iteration, never failing over on second; see fotest.c for a
|
||||
* test case that randomly switches fail-over on and off)
|
||||
*/
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
MPS_ARGS_BEGIN(piArgs) {
|
||||
MPS_ARGS_ADD(piArgs, MPS_KEY_MFS_UNIT_SIZE, sizeof(CBSFastBlockStruct));
|
||||
MPS_ARGS_ADD(piArgs, MPS_KEY_EXTEND_BY, ArenaGrainSize(arena));
|
||||
MPS_ARGS_ADD(piArgs, MFSExtendSelf, i != 0);
|
||||
die(PoolInit(mfs, arena, PoolClassMFS(), piArgs), "PoolInit");
|
||||
} MPS_ARGS_END(piArgs);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, CBSBlockPool, mfs);
|
||||
die((mps_res_t)LandInit(cbs, CLASS(CBSFast), arena, state.align,
|
||||
NULL, args),
|
||||
"failed to initialise CBS");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
die((mps_res_t)LandInit(fl, CLASS(Freelist), arena, state.align,
|
||||
NULL, mps_args_none),
|
||||
"failed to initialise Freelist");
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, FailoverPrimary, cbs);
|
||||
MPS_ARGS_ADD(args, FailoverSecondary, fl);
|
||||
die((mps_res_t)LandInit(fo, CLASS(Failover), arena, state.align,
|
||||
NULL, args),
|
||||
"failed to initialise Failover");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
state.land = fo;
|
||||
test(&state, nFOOperations, 3);
|
||||
LandFinish(fo);
|
||||
LandFinish(fl);
|
||||
LandFinish(cbs);
|
||||
PoolFinish(mfs);
|
||||
}
|
||||
|
||||
ControlFree(arena, p, (state.size + 1) * state.align);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
printf("Number of allocations attempted: %"PRIuLONGEST"\n",
|
||||
(ulongest_t)NAllocateTried);
|
||||
printf("Number of allocations succeeded: %"PRIuLONGEST"\n",
|
||||
(ulongest_t)NAllocateSucceeded);
|
||||
printf("Number of deallocations attempted: %"PRIuLONGEST"\n",
|
||||
(ulongest_t)NDeallocateTried);
|
||||
printf("Number of deallocations succeeded: %"PRIuLONGEST"\n",
|
||||
(ulongest_t)NDeallocateSucceeded);
|
||||
}
|
||||
|
||||
static void shuffle(Addr *addr, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
size_t j = rnd() % (n - i);
|
||||
Addr tmp = addr[j];
|
||||
addr[j] = addr[i];
|
||||
addr[i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_steal(void)
|
||||
{
|
||||
mps_arena_t mpsArena;
|
||||
Arena arena;
|
||||
MFSStruct mfs; /* stores blocks for the CBS */
|
||||
Pool pool = MFSPool(&mfs);
|
||||
CBSStruct cbs; /* allocated memory land */
|
||||
Land land = CBSLand(&cbs);
|
||||
Addr base;
|
||||
Addr addr[4096];
|
||||
Size grainSize;
|
||||
size_t i, n = NELEMS(addr), stolenInsert = 0, missingDelete = 0;
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
die(mps_arena_create_k(&mpsArena, mps_arena_class_vm(), args), "arena");
|
||||
} MPS_ARGS_END(args);
|
||||
arena = (Arena)mpsArena; /* avoid pun */
|
||||
grainSize = ArenaGrainSize(arena);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, sizeof(RangeTreeStruct));
|
||||
MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, grainSize);
|
||||
MPS_ARGS_ADD(args, MFSExtendSelf, FALSE);
|
||||
die(PoolInit(pool, arena, CLASS(MFSPool), args), "pool");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, CBSBlockPool, pool);
|
||||
die(LandInit(land, CLASS(CBS), arena, grainSize, NULL, args),
|
||||
"land");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
/* Allocate a range of grains. */
|
||||
die(ArenaAlloc(&base, LocusPrefDefault(), grainSize * n, pool), "alloc");
|
||||
for (i = 0; i < n; ++i)
|
||||
addr[i] = AddrAdd(base, i * grainSize);
|
||||
|
||||
/* Shuffle the grains. */
|
||||
shuffle(addr, n);
|
||||
|
||||
/* Insert grains into the land in shuffled order. */
|
||||
for (i = 0; i < n; ++i) {
|
||||
RangeStruct range, origRange, containingRange;
|
||||
RangeInitSize(&range, addr[i], grainSize);
|
||||
RangeCopy(&origRange, &range);
|
||||
die(LandInsertSteal(&containingRange, land, &range), "steal");
|
||||
if (!RangesEqual(&origRange, &range))
|
||||
++ stolenInsert;
|
||||
}
|
||||
|
||||
/* Shuffle grains again. */
|
||||
shuffle(addr, n);
|
||||
|
||||
/* Delete unstolen grains from the land in shuffled order. */
|
||||
for (i = 0; i < n; ++i) {
|
||||
RangeStruct range, containingRange;
|
||||
Res res;
|
||||
RangeInitSize(&range, addr[i], grainSize);
|
||||
res = LandDeleteSteal(&containingRange, land, &range);
|
||||
if (res == ResOK) {
|
||||
ArenaFree(addr[i], grainSize, pool);
|
||||
} else {
|
||||
Insist(res == ResFAIL); /* grain was stolen */
|
||||
++ missingDelete;
|
||||
}
|
||||
}
|
||||
|
||||
Insist(LandSize(land) == 0);
|
||||
LandFinish(land);
|
||||
Insist(PoolFreeSize(pool) == PoolTotalSize(pool));
|
||||
PoolFinish(pool);
|
||||
mps_arena_destroy(arena);
|
||||
Insist(stolenInsert <= missingDelete);
|
||||
Insist(missingDelete < n);
|
||||
printf("Stolen on insert: %"PRIuLONGEST"\n", (ulongest_t)stolenInsert);
|
||||
printf("Missing on delete: %"PRIuLONGEST"\n", (ulongest_t)missingDelete);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
testlib_init(argc, argv);
|
||||
test_land();
|
||||
test_steal();
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
352
mps/code/ld.c
Normal file
352
mps/code/ld.c
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/* ld.c: LOCATION DEPENDENCY IMPLEMENTATION
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .def: A location dependency records the fact that the bit-patterns
|
||||
* of some references will be used directly (most likely for
|
||||
* hashing), and provides a protocol for finding out whether that
|
||||
* dependency has become stale because a reference has been changed (by
|
||||
* a moving memory manager).
|
||||
*
|
||||
* .rationale: The client may build hash-tables using pointer hashing.
|
||||
* The collector may change the values of the pointers transparently,
|
||||
* by fixing them and moving the objects. The hash function will no
|
||||
* longer return the same value, and the object can't be found in
|
||||
* the expected bucket. When the client can't find an object in a
|
||||
* hashtable it must check to see if any of the references in the table
|
||||
* have moved, and rehash if they have. Location dependency provides
|
||||
* a reasonably accurate way of determining whether this has happened.
|
||||
*
|
||||
* .impl: A location dependency consists of an epoch (monotonically
|
||||
* increasing notion of time) and a reference set. The epoch records
|
||||
* when the location dependency started, and the reference set
|
||||
* accumulates an approximation to the set of references which are
|
||||
* depended on. The client can check to see if any of these
|
||||
* references have moved since the epoch.
|
||||
*
|
||||
* .history: The current epoch, and a history of object movement
|
||||
* are recorded in the arena. Each slot in the history contains a
|
||||
* summary of all the movement since an earlier epoch (maintained by
|
||||
* LDAge). To see if a dependency has become stale all that
|
||||
* is needed is to see whether its reference set intersects with the
|
||||
* movement since its epoch.
|
||||
*
|
||||
* .mod: LDHistoryLENGTH is used as a modulus to calculate the offset
|
||||
* of an epoch in the history, so it's best if this is a power of two.
|
||||
* <code/mpmconf.h>
|
||||
*
|
||||
* .epoch-size: The epoch should probably be a longer integer to avoid
|
||||
* the possibility of overflow.
|
||||
* (32 bits only gives 50 days at 1ms frequency)
|
||||
*
|
||||
* .ld.access: Accesses (reads and writes) to the ld structure must be
|
||||
* "wrapped" with an ShieldExpose/Cover pair if and only if the access
|
||||
* is taking place inside the arena. Currently this is only the case for
|
||||
* LDReset.
|
||||
*/
|
||||
|
||||
#include "mpm.h"
|
||||
|
||||
SRCID(ld, "$Id$");
|
||||
|
||||
|
||||
void HistoryInit(History history)
|
||||
{
|
||||
Index i;
|
||||
|
||||
AVER(history != NULL);
|
||||
|
||||
history->epoch = 0;
|
||||
history->prehistory = RefSetEMPTY;
|
||||
for (i = 0; i < LDHistoryLENGTH; ++i)
|
||||
history->history[i] = RefSetEMPTY;
|
||||
|
||||
history->sig = HistorySig;
|
||||
AVERT(History, history);
|
||||
}
|
||||
|
||||
Bool HistoryCheck(History history)
|
||||
{
|
||||
Index i;
|
||||
RefSet rs;
|
||||
|
||||
CHECKS(History, history);
|
||||
|
||||
/* check that each history entry is a subset of the next oldest */
|
||||
rs = RefSetEMPTY;
|
||||
/* note this loop starts from 1; there is no history age 0 */
|
||||
for (i = 1; i <= LDHistoryLENGTH; ++i) {
|
||||
/* check history age 'i'; 'j' is the history index. */
|
||||
Index j = (history->epoch + LDHistoryLENGTH - i) % LDHistoryLENGTH;
|
||||
CHECKL(RefSetSub(rs, history->history[j]));
|
||||
rs = history->history[j];
|
||||
}
|
||||
/* the oldest history entry must be a subset of the prehistory */
|
||||
CHECKL(RefSetSub(rs, history->prehistory));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void HistoryFinish(History history)
|
||||
{
|
||||
AVERT(History, history);
|
||||
history->sig = SigInvalid;
|
||||
}
|
||||
|
||||
Res HistoryDescribe(History history, mps_lib_FILE *stream, Count depth)
|
||||
{
|
||||
Res res;
|
||||
Index i;
|
||||
|
||||
if (!TESTT(History, history))
|
||||
return ResPARAM;
|
||||
if (stream == NULL)
|
||||
return ResPARAM;
|
||||
|
||||
res = WriteF(stream, depth,
|
||||
"History $P {\n", (WriteFP)history,
|
||||
" epoch = $U\n", (WriteFU)history->epoch,
|
||||
" prehistory = $B\n", (WriteFB)history->prehistory,
|
||||
" history {\n",
|
||||
" [note: indices are raw, not rotated]\n",
|
||||
NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
for (i = 0; i < LDHistoryLENGTH; ++i) {
|
||||
res = WriteF(stream, depth + 4,
|
||||
"[$U] = $B\n", (WriteFU)i, (WriteFB)history->history[i],
|
||||
NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
}
|
||||
|
||||
res = WriteF(stream, depth,
|
||||
" }\n",
|
||||
"} History $P\n", (WriteFP)history,
|
||||
NULL);
|
||||
if (res != ResOK)
|
||||
return res;
|
||||
|
||||
return ResOK;
|
||||
}
|
||||
|
||||
|
||||
/* LDReset -- reset a dependency to empty
|
||||
*
|
||||
* .reset.sync: This does not need to be synchronized with LDAge
|
||||
* because if the epoch advances after it is read the dependency
|
||||
* will simply include movement for more time than necessary.
|
||||
*/
|
||||
void LDReset(mps_ld_t ld, Arena arena)
|
||||
{
|
||||
Bool b;
|
||||
Seg seg;
|
||||
|
||||
AVER(ld != NULL);
|
||||
AVERT(Arena, arena);
|
||||
|
||||
b = SegOfAddr(&seg, arena, (Addr)ld);
|
||||
if (b)
|
||||
ShieldExpose(arena, seg); /* .ld.access */
|
||||
ld->_epoch = ArenaHistory(arena)->epoch;
|
||||
ld->_rs = RefSetEMPTY;
|
||||
if (b)
|
||||
ShieldCover(arena, seg);
|
||||
}
|
||||
|
||||
|
||||
/* LDAdd -- add a reference to a dependency
|
||||
*
|
||||
* .add.lock-free: This function is thread safe with respect to the
|
||||
* (rest of the) mps. It is unnecessary to claim locks before calling
|
||||
* this function.
|
||||
*
|
||||
* .add.user-serial:
|
||||
* However, this function is _not_ thread safe with respect to itself.
|
||||
* Users should ensure that calls to LDAdd operating on the same LD are
|
||||
* serialized.
|
||||
*
|
||||
* .add.sync: Add must take place _before_ the location of the reference
|
||||
* is depended on. If the reference changes between adding and
|
||||
* depending it will show up as moved because the movement will have
|
||||
* occurred since the epoch recorded in the dependency. If the location
|
||||
* were used first only the new location of the reference would end up
|
||||
* in the set.
|
||||
*
|
||||
* .add.no-arena-check: Add does not check that the address belongs to
|
||||
* the arena because this would require taking the arena lock. We
|
||||
* would rather that this function be lock-free even if some errors
|
||||
* are not detected.
|
||||
*
|
||||
* .add.no-align-check: Add does not check that the address is
|
||||
* aligned, for the same reason as .add.check: it can't find out which
|
||||
* pool the address belongs to without taking the lock.
|
||||
*/
|
||||
void LDAdd(mps_ld_t ld, Arena arena, Addr addr)
|
||||
{
|
||||
AVER(ld != NULL);
|
||||
AVER(TESTT(Arena, arena)); /* see .add.lock-free */
|
||||
AVER(ld->_epoch <= ArenaHistory(arena)->epoch);
|
||||
|
||||
ld->_rs = RefSetAdd(arena, ld->_rs, addr);
|
||||
}
|
||||
|
||||
|
||||
/* LDIsStaleAny -- check whether any dependency is stale
|
||||
*
|
||||
* .stale.thread-safe: This function is thread safe. It will return a
|
||||
* correct (but possibly conservative) answer regardless of the number
|
||||
* of calls to LDAge anywhere during the function. Update with care.
|
||||
*
|
||||
* .stale.current: If the dependency's epoch is the current epoch,
|
||||
* nothing can have moved since it was initialized.
|
||||
*
|
||||
* .stale.recent: If the dependency is recent, see if it intersects
|
||||
* with everything which has moved since it was initialized.
|
||||
*
|
||||
* .stale.recent.conservative: The refset from the history table is
|
||||
* loaded before we check whether ld->_epoch is "recent" with respect to
|
||||
* the current epoch. This means that we may (conservatively) decide
|
||||
* to use the prehistory instead.
|
||||
*
|
||||
* .stale.old: Otherwise, if the dependency is older than the length
|
||||
* of the history, check it against all movement that has ever occurred.
|
||||
*/
|
||||
Bool LDIsStaleAny(mps_ld_t ld, Arena arena)
|
||||
{
|
||||
History history;
|
||||
RefSet rs;
|
||||
|
||||
AVER(ld != NULL);
|
||||
AVER(TESTT(Arena, arena)); /* .stale.thread-safe */
|
||||
history = ArenaHistory(arena);
|
||||
AVER(ld->_epoch <= history->epoch);
|
||||
|
||||
if (history->epoch == ld->_epoch) /* .stale.current */
|
||||
return FALSE;
|
||||
|
||||
/* Load the history refset, _then_ check to see if it's recent.
|
||||
* This may in fact load an okay refset, which we decide to throw
|
||||
* away and use the pre-history instead. */
|
||||
rs = history->history[ld->_epoch % LDHistoryLENGTH];
|
||||
/* .stale.recent */
|
||||
/* .stale.recent.conservative */
|
||||
if (history->epoch - ld->_epoch > LDHistoryLENGTH) {
|
||||
rs = history->prehistory; /* .stale.old */
|
||||
}
|
||||
|
||||
return RefSetInter(ld->_rs, rs) != RefSetEMPTY;
|
||||
}
|
||||
|
||||
|
||||
/* LDIsStale -- check whether a particular dependency is stale
|
||||
*
|
||||
* .stale.conservative: In fact we just ignore the address and test if
|
||||
* any dependency is stale. This is conservatively correct (no false
|
||||
* negatives) but provides a hook for future improvement.
|
||||
*
|
||||
* .stale.no-arena-check: See .add.no-arena-check.
|
||||
*
|
||||
* .stale.no-align-check: See .add.no-align-check.
|
||||
*/
|
||||
Bool LDIsStale(mps_ld_t ld, Arena arena, Addr addr)
|
||||
{
|
||||
UNUSED(addr);
|
||||
return LDIsStaleAny(ld, arena);
|
||||
}
|
||||
|
||||
|
||||
/* LDAge -- age the arena by adding a moved set
|
||||
*
|
||||
* This stores the fact that a set of references has changed in
|
||||
* the history in the arena structure, and increments the epoch.
|
||||
*
|
||||
* This is only called during a 'flip', because it must be atomic
|
||||
* w.r.t. the mutator (and therefore w.r.t. LdIsStale). This is
|
||||
* because it updates the notion of the 'current' and 'oldest' history
|
||||
* entries.
|
||||
*/
|
||||
void LDAge(Arena arena, RefSet rs)
|
||||
{
|
||||
History history;
|
||||
Size i;
|
||||
|
||||
AVERT(Arena, arena);
|
||||
history = ArenaHistory(arena);
|
||||
AVER(rs != RefSetEMPTY);
|
||||
|
||||
/* Replace the entry for epoch - LDHistoryLENGTH by an empty */
|
||||
/* set which will become the set which has moved since the */
|
||||
/* current epoch. */
|
||||
history->history[history->epoch % LDHistoryLENGTH] = RefSetEMPTY;
|
||||
|
||||
/* Record the fact that the moved set has moved, by adding it */
|
||||
/* to all the sets in the history, including the set for the */
|
||||
/* current epoch. */
|
||||
for(i = 0; i < LDHistoryLENGTH; ++i)
|
||||
history->history[i] = RefSetUnion(history->history[i], rs);
|
||||
|
||||
/* This is the union of all movement since time zero. */
|
||||
history->prehistory = RefSetUnion(history->prehistory, rs);
|
||||
|
||||
/* Advance the epoch by one. */
|
||||
++history->epoch;
|
||||
AVER(history->epoch != 0); /* .epoch-size */
|
||||
}
|
||||
|
||||
|
||||
/* LDMerge -- merge two location dependencies
|
||||
*
|
||||
* .merge.lock-free: This function is thread-safe with respect to the
|
||||
* (rest of the) MPS. It is unnecessary to claim locks before calling
|
||||
* this function.
|
||||
*/
|
||||
void LDMerge(mps_ld_t ld, Arena arena, mps_ld_t from)
|
||||
{
|
||||
AVER(ld != NULL);
|
||||
AVER(TESTT(Arena, arena)); /* .merge.lock-free */
|
||||
AVER(ld->_epoch <= ArenaHistory(arena)->epoch);
|
||||
AVER(from != NULL);
|
||||
AVER(from->_epoch <= ArenaHistory(arena)->epoch);
|
||||
|
||||
/* If a reference has been added since epoch e1 then I've */
|
||||
/* certainly added since epoch e0 where e0 < e1. Therefore */
|
||||
/* the epoch of the merged ld is the minimum. */
|
||||
if (from->_epoch < ld->_epoch)
|
||||
ld->_epoch = from->_epoch;
|
||||
|
||||
/* The set of references added is the union of the two. */
|
||||
ld->_rs = RefSetUnion(ld->_rs, from->_rs);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
55
mps/code/lia6gc.gmk
Normal file
55
mps/code/lia6gc.gmk
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# lia6gc.gmk: BUILD FOR LINUX/ARM64/GCC PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2021 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = lia6gc
|
||||
|
||||
MPMPF = \
|
||||
lockix.c \
|
||||
prmcanan.c \
|
||||
prmcix.c \
|
||||
prmclia6.c \
|
||||
protix.c \
|
||||
protsgix.c \
|
||||
pthrdext.c \
|
||||
span.c \
|
||||
thix.c \
|
||||
vmix.c
|
||||
|
||||
LIBS = -lm -lpthread
|
||||
|
||||
include gc.gmk
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2021 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
55
mps/code/lia6ll.gmk
Normal file
55
mps/code/lia6ll.gmk
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# -*- makefile -*-
|
||||
#
|
||||
# lia6ll.gmk: BUILD FOR LINUX/ARM64/Clang PLATFORM
|
||||
#
|
||||
# $Id$
|
||||
# Copyright (c) 2001-2021 Ravenbrook Limited. See end of file for license.
|
||||
|
||||
PFM = lia6ll
|
||||
|
||||
MPMPF = \
|
||||
lockix.c \
|
||||
prmcanan.c \
|
||||
prmcix.c \
|
||||
prmclia6.c \
|
||||
protix.c \
|
||||
protsgix.c \
|
||||
pthrdext.c \
|
||||
span.c \
|
||||
thix.c \
|
||||
vmix.c
|
||||
|
||||
LIBS = -lm -lpthread
|
||||
|
||||
include ll.gmk
|
||||
include comm.gmk
|
||||
|
||||
|
||||
# C. COPYRIGHT AND LICENSE
|
||||
#
|
||||
# Copyright (C) 2001-2021 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue