From 3b599a0319939a3cef660aaacb4bccc23a73e88a Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Mon, 2 Sep 2019 23:23:35 -0400 Subject: [PATCH] Problem set 1 for 2019. --- .gitignore | 26 +++ README.md | 8 + pset1/.gitignore | 6 + pset1/AUTHORS.md | 17 ++ pset1/GNUmakefile | 55 ++++++ pset1/README.md | 14 ++ pset1/basealloc.cc | 92 ++++++++++ pset1/build/rules.mk | 105 +++++++++++ pset1/check.pl | 423 +++++++++++++++++++++++++++++++++++++++++++ pset1/hhtest.cc | 137 ++++++++++++++ pset1/m61.cc | 89 +++++++++ pset1/m61.hh | 95 ++++++++++ pset1/test001.cc | 12 ++ pset1/test002.cc | 15 ++ pset1/test003.cc | 17 ++ pset1/test004.cc | 17 ++ pset1/test005.cc | 34 ++++ pset1/test006.cc | 17 ++ pset1/test007.cc | 16 ++ pset1/test008.cc | 15 ++ pset1/test009.cc | 13 ++ pset1/test010.cc | 26 +++ pset1/test011.cc | 31 ++++ pset1/test012.cc | 21 +++ pset1/test013.cc | 17 ++ pset1/test014.cc | 15 ++ pset1/test015.cc | 18 ++ pset1/test016.cc | 13 ++ pset1/test017.cc | 14 ++ pset1/test018.cc | 20 ++ pset1/test019.cc | 14 ++ pset1/test020.cc | 17 ++ pset1/test021.cc | 16 ++ pset1/test022.cc | 16 ++ pset1/test023.cc | 16 ++ pset1/test024.cc | 19 ++ pset1/test025.cc | 19 ++ pset1/test026.cc | 19 ++ pset1/test027.cc | 17 ++ pset1/test028.cc | 31 ++++ pset1/test029.cc | 38 ++++ pset1/test030.cc | 36 ++++ pset1/test031.cc | 15 ++ pset1/test032.cc | 19 ++ pset1/test033.cc | 21 +++ pset1/test034.cc | 85 +++++++++ pset1/test035.cc | 19 ++ pset1/test036.cc | 22 +++ pset1/test037.cc | 36 ++++ pset1/test038.cc | 27 +++ pset1/test039.cc | 31 ++++ 51 files changed, 1931 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pset1/.gitignore create mode 100644 pset1/AUTHORS.md create mode 100644 pset1/GNUmakefile create mode 100644 pset1/README.md create mode 100644 pset1/basealloc.cc create mode 100644 pset1/build/rules.mk create mode 100644 pset1/check.pl create mode 100644 pset1/hhtest.cc create mode 100644 pset1/m61.cc create mode 100644 pset1/m61.hh create mode 100644 pset1/test001.cc create mode 100644 pset1/test002.cc create mode 100644 pset1/test003.cc create mode 100644 pset1/test004.cc create mode 100644 pset1/test005.cc create mode 100644 pset1/test006.cc create mode 100644 pset1/test007.cc create mode 100644 pset1/test008.cc create mode 100644 pset1/test009.cc create mode 100644 pset1/test010.cc create mode 100644 pset1/test011.cc create mode 100644 pset1/test012.cc create mode 100644 pset1/test013.cc create mode 100644 pset1/test014.cc create mode 100644 pset1/test015.cc create mode 100644 pset1/test016.cc create mode 100644 pset1/test017.cc create mode 100644 pset1/test018.cc create mode 100644 pset1/test019.cc create mode 100644 pset1/test020.cc create mode 100644 pset1/test021.cc create mode 100644 pset1/test022.cc create mode 100644 pset1/test023.cc create mode 100644 pset1/test024.cc create mode 100644 pset1/test025.cc create mode 100644 pset1/test026.cc create mode 100644 pset1/test027.cc create mode 100644 pset1/test028.cc create mode 100644 pset1/test029.cc create mode 100644 pset1/test030.cc create mode 100644 pset1/test031.cc create mode 100644 pset1/test032.cc create mode 100644 pset1/test033.cc create mode 100644 pset1/test034.cc create mode 100644 pset1/test035.cc create mode 100644 pset1/test036.cc create mode 100644 pset1/test037.cc create mode 100644 pset1/test038.cc create mode 100644 pset1/test039.cc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf7b395 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +#*# +*.aux +*.backup +*.core +*.dSYM +*.dvi +*.gcda +*.log +*.noopt +*.o +*.optimized +*.unsafe +*~ +.DS_Store +.cproject +.deps +.gitcheckout +.goutputstream-* +.project +a.out +core* +strace.out +tags +tags.* +typescript +vgcore* diff --git a/README.md b/README.md new file mode 100644 index 0000000..9408a45 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +CS 61 Problem Sets +================== + +This repository contains the problem sets for Harvard’s CS 61 class, Systems +Programming and Machine Organization. + +For more information, see the course wiki: +https://cs61.seas.harvard.edu/ diff --git a/pset1/.gitignore b/pset1/.gitignore new file mode 100644 index 0000000..b83006a --- /dev/null +++ b/pset1/.gitignore @@ -0,0 +1,6 @@ +*.dSYM +*.o +.deps +hhtest +out +test[0-9][0-9][0-9] diff --git a/pset1/AUTHORS.md b/pset1/AUTHORS.md new file mode 100644 index 0000000..0fb1ade --- /dev/null +++ b/pset1/AUTHORS.md @@ -0,0 +1,17 @@ +Author and collaborators +======================== + +Primary student +--------------- +(Your name.) + + +Collaborators +------------- +(List any other collaborators and describe help you got from other students +in the class.) + + +Citations +--------- +(List any other sources consulted.) diff --git a/pset1/GNUmakefile b/pset1/GNUmakefile new file mode 100644 index 0000000..79f2e67 --- /dev/null +++ b/pset1/GNUmakefile @@ -0,0 +1,55 @@ +# Default optimization level +O ?= 2 + +TESTS = $(patsubst %.cc,%,$(sort $(wildcard test[0-9][0-9][0-9].cc))) + +all: $(TESTS) hhtest + +-include build/rules.mk + +LIBS = -lm + +%.o: %.cc $(BUILDSTAMP) + $(call run,$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPCFLAGS) $(O) -o $@ -c,COMPILE,$<) + +all: + @echo "*** Run 'make check' or 'make check-all' to check your work." + +test%: m61.o basealloc.o test%.o + $(call run,$(CXX) $(CXXFLAGS) $(O) -o $@ $^ $(LDFLAGS) $(LIBS),LINK $@) + +hhtest: m61.o basealloc.o hhtest.o + $(call run,$(CXX) $(CXXFLAGS) $(O) -o $@ $^ $(LDFLAGS) $(LIBS),LINK $@) + +check: $(patsubst %,run-%,$(TESTS)) + @echo "*** All tests succeeded!" + +check-all: $(TESTS) + @good=true; for i in $(TESTS); do $(MAKE) --no-print-directory run-$$i || good=false; done; \ + if $$good; then echo "*** All tests succeeded!"; fi; $$good + +check-%: + @any=false; good=true; for i in `perl check.pl -e "$*"`; do \ + any=true; $(MAKE) run-$$i || good=false; done; \ + if $$any; then $$good; else echo "*** No such test" 1>&2; $$any; fi + +run-: + @echo "*** No such test" 1>&2; exit 1 + +run-%: % + @test -d out || mkdir out + @perl check.pl -x $< + +clean: clean-main +clean-main: + $(call run,rm -f $(TESTS) hhtest *.o core *.core,CLEAN) + $(call run,rm -rf out *.dSYM $(DEPSDIR)) + +distclean: clean + +MALLOC_CHECK_=0 +export MALLOC_CHECK_ + +.PRECIOUS: %.o +.PHONY: all clean clean-main clean-hook distclean \ + run run- run% prepare-check check check-all check-% diff --git a/pset1/README.md b/pset1/README.md new file mode 100644 index 0000000..b0b0c7b --- /dev/null +++ b/pset1/README.md @@ -0,0 +1,14 @@ +CS 61 Problem Set 1 +=================== + +**Fill out both this file and `AUTHORS.md` before submitting.** We grade +anonymously, so put all personally identifying information, including +collaborators, in `AUTHORS.md`. + +Grading notes (if any) +---------------------- + + + +Extra credit attempted (if any) +------------------------------- diff --git a/pset1/basealloc.cc b/pset1/basealloc.cc new file mode 100644 index 0000000..100b9ee --- /dev/null +++ b/pset1/basealloc.cc @@ -0,0 +1,92 @@ +#define M61_DISABLE 1 +#include "m61.hh" +#include +#include +#include + + +// This file contains a base memory allocator guaranteed not to +// overwrite freed allocations. No need to understand it. + + +using base_allocation = std::pair; + +// `allocs` is a hash table mapping active pointer address to allocation size. +// `frees` is a vector of freed allocations. +static std::unordered_map allocs; +static std::vector frees; +static int disabled; + +static unsigned alloc_random() { + static uint64_t x = 8973443640547502487ULL; + x = x * 6364136223846793005ULL + 1ULL; + return x >> 32; +} + +static void base_allocator_atexit(); + +void* base_malloc(size_t sz) { + if (disabled) { + return malloc(sz); + } + ++disabled; + uintptr_t ptr = 0; + + static int base_alloc_atexit_installed = 0; + if (!base_alloc_atexit_installed) { + atexit(base_allocator_atexit); + base_alloc_atexit_installed = 1; + } + + // try to use a previously-freed block 75% of the time + unsigned r = alloc_random(); + if (r % 4 != 0) { + for (unsigned ntries = 0; ntries < 10 && ntries < frees.size(); ++ntries) { + auto& f = frees[alloc_random() % frees.size()]; + if (f.second >= sz) { + allocs.insert(f); + ptr = f.first; + f = frees.back(); + frees.pop_back(); + break; + } + } + } + + if (!ptr) { + // need a new allocation + ptr = reinterpret_cast(malloc(sz ? sz : 1)); + } + if (ptr) { + allocs[reinterpret_cast(ptr)] = sz; + } + + --disabled; + return reinterpret_cast(ptr); +} + +void base_free(void* ptr) { + if (disabled || !ptr) { + free(ptr); + } else { + // mark free if found; if not found, invalid free: silently ignore + ++disabled; + auto it = allocs.find(reinterpret_cast(ptr)); + if (it != allocs.end()) { + frees.push_back(*it); + allocs.erase(it); + } + --disabled; + } +} + +void base_allocator_disable(bool d) { + disabled = d; +} + +static void base_allocator_atexit() { + // clean up freed memory to shut up leak detector + for (auto& alloc : frees) { + free(reinterpret_cast(alloc.first)); + } +} diff --git a/pset1/build/rules.mk b/pset1/build/rules.mk new file mode 100644 index 0000000..faaa4a7 --- /dev/null +++ b/pset1/build/rules.mk @@ -0,0 +1,105 @@ +# are we using clang? +ISCLANG := $(shell if $(CC) --version | grep -e 'LLVM\|clang' >/dev/null; then echo 1; fi) +ISLINUX := $(if $(wildcard /usr/include/linux/*.h),1,) + +CFLAGS := -std=gnu11 -W -Wall -Wshadow -Wno-unused-command-line-argument -g $(DEFS) $(CFLAGS) +CXXFLAGS := -std=gnu++1z -W -Wall -Wshadow -Wno-unused-command-line-argument -g $(DEFS) $(CXXFLAGS) +O ?= -O3 +ifeq ($(filter 0 1 2 3 s,$(O)$(NOOVERRIDEO)),$(strip $(O))) +override O := -O$(O) +endif +LDFLAGS := -no-pie + +# sanitizer arguments +ifndef SAN +SAN := $(SANITIZE) +endif +ifndef TSAN + ifeq ($(WANT_TSAN),1) +TSAN := $(SAN) + endif +endif + +check_for_sanitizer = $(if $(strip $(shell $(CC) -fsanitize=$(1) -x c -E /dev/null 2>&1 | grep sanitize=)),$(info ** WARNING: The `$(CC)` compiler does not support `-fsanitize=$(1)`.),1) +ifeq ($(TSAN),1) + ifeq ($(or $(ISCLANG),$(filter-out 3.% 4.% 5.% 6.%,$(shell $(CC) -dumpversion)),$(filter-out Linux%,$(shell uname))),) +$(info ** WARNING: If ThreadSanitizer fails, try `make SAN=1 CC=clang`.) + endif + ifeq ($(call check_for_sanitizer,thread),1) +CFLAGS += -fsanitize=thread +CXXFLAGS += -fsanitize=thread + endif +else + ifeq ($(or $(ASAN),$(SAN)),1) + ifeq ($(call check_for_sanitizer,address),1) +CFLAGS += -fsanitize=address +CXXFLAGS += -fsanitize=address + endif + endif + ifeq ($(or $(LSAN),$(LEAKSAN)),1) + ifeq ($(call check_for_sanitizer,leak),1) +CFLAGS += -fsanitize=leak +CXXFLAGS += -fsanitize=leak + endif + endif +endif +ifeq ($(or $(UBSAN),$(SAN)),1) + ifeq ($(call check_for_sanitizer,undefined),1) +CFLAGS += -fsanitize=undefined +CXXFLAGS += -fsanitize=undefined + endif +endif + +# profiling +ifeq ($(or $(PROFILE),$(PG)),1) +CFLAGS += -pg +CXXFLAGS += -pg +endif + +# these rules ensure dependencies are created +DEPCFLAGS = -MD -MF $(DEPSDIR)/$*.d -MP +DEPSDIR := .deps +BUILDSTAMP := $(DEPSDIR)/rebuildstamp +DEPFILES := $(wildcard $(DEPSDIR)/*.d) +ifneq ($(DEPFILES),) +include $(DEPFILES) +endif + +# when the C compiler or optimization flags change, rebuild all objects +ifneq ($(strip $(DEP_CC)),$(strip $(CC) $(CPPFLAGS) $(CFLAGS) $(O))) +DEP_CC := $(shell mkdir -p $(DEPSDIR); echo >$(BUILDSTAMP); echo "DEP_CC:=$(CC) $(CPPFLAGS) $(CFLAGS) $(O)" >$(DEPSDIR)/_cc.d) +endif +ifneq ($(strip $(DEP_CXX)),$(strip $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(O))) +DEP_CXX := $(shell mkdir -p $(DEPSDIR); echo >$(BUILDSTAMP); echo "DEP_CXX:=$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(O)" >$(DEPSDIR)/_cxx.d) +endif + + +V = 0 +ifeq ($(V),1) +run = $(1) $(3) +xrun = /bin/echo "$(1) $(3)" && $(1) $(3) +else +run = @$(if $(2),/bin/echo " $(2) $(3)" &&,) $(1) $(3) +xrun = $(if $(2),/bin/echo " $(2) $(3)" &&,) $(1) $(3) +endif +runquiet = @$(1) $(3) + +# cancel implicit rules we don't want +%: %.c +%.o: %.c +%: %.cc +%.o: %.cc +%: %.o + +$(BUILDSTAMP): + @mkdir -p $(@D) + @echo >$@ + +always: + @: + +clean-hook: + @: + +.PHONY: always clean-hook +.PRECIOUS: %.o diff --git a/pset1/check.pl b/pset1/check.pl new file mode 100644 index 0000000..59ea2a8 --- /dev/null +++ b/pset1/check.pl @@ -0,0 +1,423 @@ +#! /usr/bin/perl -w +use Time::HiRes; +use POSIX; +use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); + +my($Red, $Redctx, $Green, $Greenctx, $Cyan, $Off) = ("\x1b[01;31m", "\x1b[0;31m", "\x1b[01;32m", "\x1b[0;32m", "\x1b[01;36m", "\x1b[0m"); +$Red = $Redctx = $Green = $Greenctx = $Cyan = $Off = "" if !-t STDERR || !-t STDOUT; +my($ContextLines, $LeakCheck, $Make, $Test, $Exec) = (1, 0, 0, 0, 0); +my(@Restrict, @Makeargs); + +$SIG{"CHLD"} = sub {}; +my($run61_pid); + +sub run_sh61_pipe ($$;$) { + my($text, $fd, $size) = @_; + my($n, $buf) = (0, ""); + return $text if !defined($fd); + while ((!defined($size) || length($text) <= $size) + && defined(($n = POSIX::read($fd, $buf, 8192))) + && $n > 0) { + $text .= substr($buf, 0, $n); + } + return $text; +} + +sub run_sh61 ($;%) { + my($command, %opt) = @_; + my($outfile) = exists($opt{"stdout"}) ? $opt{"stdout"} : undef; + my($size_limit_file) = exists($opt{"size_limit_file"}) ? $opt{"size_limit_file"} : $outfile; + $size_limit_file = [$size_limit_file] if $size_limit_file && !ref($size_limit_file); + my($size_limit) = exists($opt{"size_limit"}) ? $opt{"size_limit"} : undef; + my($dir) = exists($opt{"dir"}) ? $opt{"dir"} : undef; + if (defined($dir) && $size_limit_file) { + $dir =~ s{/+$}{}; + $size_limit_file = [map { m{^/} ? $_ : "$dir/$_" } @$size_limit_file]; + } + pipe(OR, OW) or die "pipe"; + fcntl(OR, F_SETFL, fcntl(OR, F_GETFL, 0) | O_NONBLOCK); + 1 while waitpid(-1, WNOHANG) > 0; + + $run61_pid = fork(); + if ($run61_pid == 0) { + POSIX::setpgid(0, 0) or die("child setpgid: $!\n"); + defined($dir) && chdir($dir); + + my($fn) = defined($opt{"stdin"}) ? $opt{"stdin"} : "/dev/null"; + if (defined($fn) && $fn ne "/dev/stdin") { + my($fd) = POSIX::open($fn, O_RDONLY); + POSIX::dup2($fd, 0); + POSIX::close($fd) if $fd != 0; + } + + close(OR); + open(OW, ">", $outfile) || die if defined($outfile) && $outfile ne "pipe"; + POSIX::dup2(fileno(OW), 1); + POSIX::dup2(fileno(OW), 2); + close(OW) if fileno(OW) != 1 && fileno(OW) != 2; + + fcntl(STDIN, F_SETFD, fcntl(STDIN, F_GETFD, 0) & ~FD_CLOEXEC); + fcntl(STDOUT, F_SETFD, fcntl(STDOUT, F_GETFD, 0) & ~FD_CLOEXEC); + fcntl(STDERR, F_SETFD, fcntl(STDERR, F_GETFD, 0) & ~FD_CLOEXEC); + + { exec($command) }; + print STDERR "error trying to run $command: $!\n"; + exit(1); + } + + POSIX::setpgid($run61_pid, $run61_pid) or die("setpgid: $!\n"); + + my($before) = Time::HiRes::time(); + my($died) = 0; + my($max_time) = exists($opt{"time_limit"}) ? $opt{"time_limit"} : 0; + my($out, $buf, $nb) = ("", ""); + my($answer) = exists($opt{"answer"}) ? $opt{"answer"} : {}; + $answer->{"command"} = $command; + + close(OW); + + eval { + do { + Time::HiRes::usleep(300000); + if (waitpid($run61_pid, WNOHANG) > 0) { + $answer->{"status"} = $?; + die "!"; + } + if (defined($size_limit) && $size_limit_file && @$size_limit_file) { + my($len) = 0; + $out = run_sh61_pipe($out, fileno(OR), $size_limit); + foreach my $fname (@$size_limit_file) { + $len += ($fname eq "pipe" ? length($out) : -s $fname); + } + if ($len > $size_limit) { + $died = "output file size $len, expected <= $size_limit"; + die "!"; + } + } + } while (Time::HiRes::time() < $before + $max_time); + $died = sprintf("timeout after %.2fs", $max_time) + if waitpid($run61_pid, WNOHANG) <= 0; + }; + + my($delta) = Time::HiRes::time() - $before; + $answer->{"time"} = $delta; + + if (exists($answer->{"status"}) && exists($opt{"delay"}) && $opt{"delay"} > 0) { + Time::HiRes::usleep($opt{"delay"} * 1e6); + } + if (exists($opt{"nokill"})) { + $answer->{"pgrp"} = $run61_pid; + } else { + kill 9, -$run61_pid; + } + $run61_pid = 0; + + if ($died) { + $answer->{"killed"} = $died; + close(OR); + return $answer; + } + + if (defined($outfile) && $outfile ne "pipe") { + $out = ""; + close(OR); + open(OR, "<", (defined($dir) ? "$dir/$outfile" : $outfile)); + } + $answer->{"output"} = run_sh61_pipe($out, fileno(OR), $size_limit); + close(OR); + + return $answer; +} + +sub read_expected ($) { + my($fname) = @_; + open(EXPECTED, $fname) or die; + + my(@expected); + my($line, $skippable, $unordered) = (0, 0, 0); + my($allow_asan_warning, $time) = (0, 0); + while (defined($_ = )) { + ++$line; + if (m{^//! \?\?\?\s+$}) { + $skippable = 1; + } elsif (m{^//!!UNORDERED\s+$}) { + $unordered = 1; + } elsif (m{^//!!TIME\s+$}) { + $time = 1; + } elsif (m{^//!!(ALLOW_|DISALLOW_)ASAN_WARNING\s*$}) { + $allow_asan_warning = $1 eq "ALLOW_"; + } elsif (m{^//! }) { + s{^....(.*?)\s*$}{$1}; + $allow_asan_warning = 1 if /^alloc count:.*fail +[1-9]/; + my($m) = {"t" => $_, "line" => $line, "skip" => $skippable, + "r" => "", "match" => []}; + foreach my $x (split(/(\?\?\?|\?\?\{.*?\}(?:=\w+)?\?\?|\?\?>=\d+\?\?)/)) { + if ($x eq "???") { + $m->{r} =~ s{(?:\\ )+\z}{\\s+}; + $m->{r} .= ".*"; + } elsif ($x =~ /\A\?\?\{(.*)\}=(\w+)\?\?\z/) { + $m->{r} .= "(" . $1 . ")"; + push @{$m->{match}}, $2; + } elsif ($x =~ /\A\?\?\{(.*)\}\?\?\z/) { + my($contents) = $1; + $m->{r} =~ s{(?:\\ )+\z}{\\s+}; + $m->{r} .= "(?:" . $contents . ")"; + } elsif ($x =~ /\A\?\?>=(\d+)\?\?\z/) { + my($contents) = $1; + $contents =~ s/\A0+(?=[1-9]|0\z)//; + $m->{r} =~ s{(?:\\ )+\z}{\\s+}; + my(@dig) = split(//, $contents); + my(@y) = ("0*[1-9]\\d{" . (@dig) . ",}"); + for (my $i = 0; $i < @dig; ++$i) { + my(@xdig) = @dig; + if ($i == @dig - 1) { + $xdig[$i] = "[" . $dig[$i] . "-9]"; + } else { + next if $dig[$i] eq "9"; + $xdig[$i] = "[" . ($dig[$i] + 1) . "-9]"; + } + for (my $j = $i + 1; $j < @dig; ++$j) { + $xdig[$j] = "\\d"; + } + push @y, "0*" . join("", @xdig); + } + $m->{r} .= "(?:" . join("|", @y) . ")(?!\\d)"; + } else { + $m->{r} .= quotemeta($x); + } + } + push @expected, $m; + $skippable = 0; + } + } + return {"l" => \@expected, "nl" => scalar(@expected), + "skip" => $skippable, "unordered" => $unordered, "time" => $time, + "allow_asan_warning" => $allow_asan_warning}; +} + +sub read_actual ($) { + my($fname) = @_; + open(ACTUAL, $fname) or die; + my(@actual); + while (defined($_ = )) { + chomp; + push @actual, $_; + } + close ACTUAL; + \@actual; +} + +sub run_compare_print_actual ($$$) { + my($actual, $a, $aname) = @_; + my($apfx) = "$aname:" . ($a + 1); + my($sep) = sprintf("$Off\n $Redctx%" . length($apfx) . "s ", ""); + my($context) = $actual->[$a]; + for (my $i = 2; $i <= $ContextLines && $a + 1 != @$actual; ++$a) { + $context .= $sep . $actual->[$a + 1]; + } + print STDERR " $Redctx", $apfx, ": Got `", $context, "`$Off\n"; +} + +sub compare_test_line ($$$) { + my($line, $expline, $chunks) = @_; + my($rex) = $expline->{r}; + while (my($k, $v) = each %$chunks) { + $rex =~ s{\\\?\\\?$k\\\?\\\?}{$v}g; + } + if ($line =~ m{\A$rex\z}) { + for (my $i = 0; $i < @{$expline->{match}}; ++$i) { + $chunks->{$expline->{match}->[$i]} = ${$i + 1}; + } + return 1; + } else { + return 0; + } +} + +sub run_compare ($$$$$$) { + my($actual, $exp, $aname, $ename, $outname, $out) = @_; + my($unordered) = $exp->{unordered}; + $outname .= " " if $outname; + + my(%chunks); + my($a) = 0; + my(@explines) = @{$exp->{l}}; + for (; $a != @$actual; ++$a) { + $_ = $actual->[$a]; + if (/^==\d+==\s*WARNING: AddressSanitizer failed to allocate/ + && $exp->{allow_asan_warning}) { + next; + } + + if (!@explines && !$exp->{skip}) { + my($lines) = $exp->{nl} == 1 ? "line" : "lines"; + print STDERR "$Red${outname}FAIL: Too much output (expected ", $exp->{nl}, " output $lines)$Off\n"; + run_compare_print_actual($actual, $a, $aname); + return 1; + } elsif (!@explines) { + next; + } + + my($ok, $e) = (0, 0); + while ($e != @explines) { + $ok = compare_test_line($_, $explines[$e], \%chunks); + last if $ok || !$unordered; + ++$e; + } + if ($ok) { + splice @explines, $e, 1; + } elsif (!$explines[0]->{skip}) { + print STDERR "$Red${outname}FAIL: Unexpected output starting on line ", $a + 1, "$Off\n"; + print STDERR " $Redctx$ename:", $explines[0]->{line}, ": Expected `", $explines[0]->{t}, "`\n"; + run_compare_print_actual($actual, $a, $aname); + return 1; + } + } + + if (@explines) { + print STDERR "$Red${outname}FAIL: Missing output starting on line ", scalar(@$actual), "$Off\n"; + print STDERR " $Redctx$ename:", $explines[0]->{line}, ": Expected `", $explines[0]->{t}, "`$Off\n"; + return 1; + } else { + my($ctx) = ""; + if ($exp->{time}) { + $ctx = "$Greenctx in " . sprintf("%.03f sec", $out->{time}); + } + print STDERR "$Green${outname}OK$ctx$Off\n"; + return 0; + } +} + +while (@ARGV > 0) { + if ($ARGV[0] eq "-c" && @ARGV > 1 && $ARGV[1] =~ /^\d+$/) { + $ContextLines = +$ARGV[1]; + shift @ARGV; + } elsif ($ARGV[0] =~ /^-c(\d+)$/) { + $ContextLines = +$1; + } elsif ($ARGV[0] eq "-l") { + $LeakCheck = 1; + } elsif ($ARGV[0] eq "-r" && @ARGV > 1) { + push @Restrict, $ARGV[1]; + shift @ARGV; + } elsif ($ARGV[0] =~ /^-r(.+)$/) { + push @Restrict, $1; + } elsif ($ARGV[0] eq "-m") { + $Make = 1; + } elsif ($ARGV[0] eq "-e") { + $Test = 1; + } elsif ($ARGV[0] eq "-x" && @ARGV > 1) { + $Exec = $ARGV[1]; + shift @ARGV; + } elsif ($ARGV[0] =~ /^-x(.+)$/) { + $Exec = $1; + } elsif ($ARGV[0] =~ /=/) { + push @Makeargs, $ARGV[0]; + } elsif ($ARGV[0] =~ /\A-/) { + print STDERR "Usage: ./check.pl [-c CONTEXT] [-l] [TESTS...]\n"; + print STDERR " ./check.pl -x EXECFILE\n"; + print STDERR " ./check.pl -e TESTS...\n"; + exit 1; + } else { + last; + } + shift @ARGV; +} + +for (my $i = 0; $i < @ARGV; ) { + if ($ARGV[$i] =~ /=/) { + push @Makeargs, $ARGV[$i]; + splice @ARGV, $i, 1; + } else { + ++$i; + } +} + +sub test_class ($;@) { + my($test) = shift @_; + foreach my $x (@_) { + if ($test eq $x + || ($x =~ m{(?:\A|[,\s])(\d+)-(\d+)(?:[,\s]|\z)} && $test >= $1 && $test <= $2) + || $x =~ m{(?:\A|[,\s])$test(?:[,\s]|\z)} + || ($x =~ m{(?:\A|[,\s])san(?:[,\s]|\z)}i && $test >= 1 && $test <= 26) + || ($x =~ m{(?:\A|[,\s])leak(?:[,\s]|\z)}i && grep { $_ == $test } (1, 8, 10, 11, 14, 15, 24, 28, 34, 35, 36, 38))) { + return 1; + } + } + 0; +} + +sub asan_options ($) { + my($test) = @_; + $test = int($1) if $test =~ m{\A(?:\./)?test(\d+)\z}; + if ($LeakCheck && test_class($test, "leak")) { + return "allocator_may_return_null=1 detect_leaks=1"; + } else { + return "allocator_may_return_null=1 detect_leaks=0"; + } +} + +sub test_runnable ($) { + my($number) = @_; + foreach my $r (@Restrict) { + return 0 if !test_class($number, $r); + } + return !@ARGV || test_class($number, @ARGV); +} + +if ($Exec) { + die "bad -x option\n" if $Exec !~ m{\A(?:\./)?[^./][^/]+\z}; + $ENV{"ASAN_OPTIONS"} = asan_options($Exec); + $out = run_sh61("./" . $Exec, "stdout" => "pipe", "stdin" => "/dev/null", + "time_limit" => 10, "size_limit" => 80000); + my($ofile) = "out/" . $Exec . ".output"; + if (open(OUT, ">", $ofile)) { + print OUT $out->{output}; + close OUT; + } + exit(run_compare([split("\n", $out->{"output"})], + read_expected($Exec . ".cc"), + $ofile, $Exec . ".cc", $Exec, $out)); +} else { + my($maxtest, $ntest, $ntestfailed) = (39, 0, 0); + if ($Test) { + for ($i = 1; $i <= $maxtest; $i += 1) { + printf "test%03d\n", $i if test_runnable($i) + } + exit; + } + if ($Make) { + my(@makeargs) = ("make", @Makeargs); + for ($i = 1; $i <= $maxtest; $i += 1) { + push @makeargs, sprintf("test%03d", $i) if test_runnable($i); + } + system(@makeargs); + exit 1 if $? != 0; + } + $ENV{"MALLOC_CHECK_"} = 0; + for ($i = 1; $i <= $maxtest; $i += 1) { + next if !test_runnable($i); + ++$ntest; + $ENV{"ASAN_OPTIONS"} = asan_options($i); + printf STDERR "test%03d ", $i; + $out = run_sh61(sprintf("./test%03d", $i), "stdout" => "pipe", "stdin" => "/dev/null", + "time_limit" => $i == 34 ? 10 : 5, "size_limit" => 8000); + if (exists($out->{killed})) { + print STDERR "${Red}CRASH: $out->{killed}$Off\n"; + if (exists($out->{output}) && $out->{output} =~ m/\A\s*(.+)/) { + print STDERR " ${Redctx}1st line of output: $1$Off\n"; + } + ++$ntestfailed; + } else { + ++$ntestfailed if run_compare([split("\n", $out->{"output"})], + read_expected(sprintf("test%03d.cc", $i)), + "output", sprintf("test%03d.cc", $i), "", $out); + } + } + my($ntestpassed) = $ntest - $ntestfailed; + if ($ntest == $maxtest && $ntestpassed == $ntest) { + print STDERR "${Green}All tests passed!$Off\n"; + } else { + my($color) = ($ntestpassed == 0 ? $Red : ($ntestpassed == $ntest ? $Green : $Cyan)); + print STDERR "${color}$ntestpassed of $ntest ", ($ntest == 1 ? "test" : "tests"), " passed$Off\n"; + } +} diff --git a/pset1/hhtest.cc b/pset1/hhtest.cc new file mode 100644 index 0000000..ba23c7e --- /dev/null +++ b/pset1/hhtest.cc @@ -0,0 +1,137 @@ +#include "m61.hh" +#include +#include +#include +#include +#define NALLOCATORS 40 +// hhtest: A sample framework for evaluating heavy hitter reports. + +// 40 different allocation functions give 40 different call sites +void f00(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f01(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f02(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f03(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f04(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f05(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f06(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f07(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f08(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f09(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f10(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f11(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f12(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f13(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f14(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f15(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f16(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f17(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f18(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f19(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f20(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f21(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f22(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f23(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f24(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f25(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f26(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f27(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f28(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f29(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f30(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f31(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f32(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f33(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f34(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f35(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f36(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f37(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f38(size_t sz) { void* ptr = malloc(sz); free(ptr); } +void f39(size_t sz) { void* ptr = malloc(sz); free(ptr); } + +// An array of those allocation functions +void (*allocators[])(size_t) = { + &f00, &f01, &f02, &f03, &f04, &f05, &f06, &f07, &f08, &f09, + &f10, &f11, &f12, &f13, &f14, &f15, &f16, &f17, &f18, &f19, + &f20, &f21, &f22, &f23, &f24, &f25, &f26, &f27, &f28, &f29, + &f30, &f31, &f32, &f33, &f34, &f35, &f36, &f37, &f38, &f39 +}; + +// Sizes passed to those allocation functions. +// Later allocation functions have much bigger sizes. +size_t sizes[NALLOCATORS] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 4, 8, 16, 32, 64, + 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 +}; + +static void phase(double skew, unsigned long long count) { + // Calculate the probability we'll call allocator I. + // That probability equals 2^(-I*skew) / \sum_{i=0}^40 2^(-I*skew). + // When skew=0, every allocator is called with equal probability. + // When skew=1, the first allocator is called twice as often as the second, + // which is called twice as often as the third, and so forth. + // When skew=-1, the first allocator is called HALF as often as the second, + // which is called HALF as often as the third, and so forth. + double sum_p = 0; + for (int i = 0; i < NALLOCATORS; ++i) { + sum_p += pow(0.5, i * skew); + } + long limit[NALLOCATORS]; + double ppos = 0; + for (int i = 0; i < NALLOCATORS; ++i) { + ppos += pow(0.5, i * skew); + limit[i] = RAND_MAX * (ppos / sum_p); + } + // Now the probability we call allocator I equals + // (limit[i] - limit[i-1]) / (double) RAND_MAX, + // if we pretend that limit[-1] == 0. + + // Pick `count` random allocators and call them. + for (unsigned long long i = 0; i < count; ++i) { + long x = random(); + int r = 0; + while (r < NALLOCATORS - 1 && x > limit[r]) { + ++r; + } + allocators[r](sizes[r]); + } +} + +int main(int argc, char **argv) { + // use the system allocator, not the base allocator + // (the base allocator can be slow) + base_allocator_disable(1); + + if (argc > 1 && (strcmp(argv[1], "-h") == 0 + || strcmp(argv[1], "--help") == 0)) { + printf("Usage: ./hhtest\n\ + OR ./hhtest SKEW [COUNT]\n\ + OR ./hhtest SKEW1 COUNT1 SKEW2 COUNT2 ...\n\ +\n\ + Each SKEW is a real number. 0 means each allocator is called equally\n\ + frequently. 1 means the first allocator is called twice as much as the\n\ + second, and so on. The default SKEW is 0.\n\ +\n\ + Each COUNT is a positive integer. It says how many allocations are made.\n\ + The default is 1000000.\n\ +\n\ + If you give multiple SKEW COUNT pairs, then ./hhtest runs several\n\ + allocation phases in order.\n"); + exit(0); + } + + // parse arguments and run phases + for (int position = 1; position == 1 || position < argc; position += 2) { + double skew = 0; + if (position < argc) { + skew = strtod(argv[position], 0); + } + + unsigned long long count = 1000000; + if (position + 1 < argc) { + count = strtoull(argv[position + 1], 0, 0); + } + + phase(skew, count); + } +} diff --git a/pset1/m61.cc b/pset1/m61.cc new file mode 100644 index 0000000..4f333e5 --- /dev/null +++ b/pset1/m61.cc @@ -0,0 +1,89 @@ +#define M61_DISABLE 1 +#include "m61.hh" +#include +#include +#include +#include +#include + +/// m61_malloc(sz, file, line) +/// Return a pointer to `sz` bytes of newly-allocated dynamic memory. +/// The memory is not initialized. If `sz == 0`, then m61_malloc must +/// return a unique, newly-allocated pointer value. The allocation +/// request was at location `file`:`line`. + +void* m61_malloc(size_t sz, const char* file, long line) { + (void) file, (void) line; // avoid uninitialized variable warnings + // Your code here. + return base_malloc(sz); +} + + +/// m61_free(ptr, file, line) +/// Free the memory space pointed to by `ptr`, which must have been +/// returned by a previous call to m61_malloc. If `ptr == NULL`, +/// does nothing. The free was called at location `file`:`line`. + +void m61_free(void* ptr, const char* file, long line) { + (void) file, (void) line; // avoid uninitialized variable warnings + // Your code here. + base_free(ptr); +} + + +/// m61_calloc(nmemb, sz, file, line) +/// Return a pointer to newly-allocated dynamic memory big enough to +/// hold an array of `nmemb` elements of `sz` bytes each. If `sz == 0`, +/// then must return a unique, newly-allocated pointer value. Returned +/// memory should be initialized to zero. The allocation request was at +/// location `file`:`line`. + +void* m61_calloc(size_t nmemb, size_t sz, const char* file, long line) { + // Your code here (to fix test014). + void* ptr = m61_malloc(nmemb * sz, file, line); + if (ptr) { + memset(ptr, 0, nmemb * sz); + } + return ptr; +} + + +/// m61_get_statistics(stats) +/// Store the current memory statistics in `*stats`. + +void m61_get_statistics(m61_statistics* stats) { + // Stub: set all statistics to enormous numbers + memset(stats, 255, sizeof(m61_statistics)); + // Your code here. +} + + +/// m61_print_statistics() +/// Print the current memory statistics. + +void m61_print_statistics() { + m61_statistics stats; + m61_get_statistics(&stats); + + printf("alloc count: active %10llu total %10llu fail %10llu\n", + stats.nactive, stats.ntotal, stats.nfail); + printf("alloc size: active %10llu total %10llu fail %10llu\n", + stats.active_size, stats.total_size, stats.fail_size); +} + + +/// m61_print_leak_report() +/// Print a report of all currently-active allocated blocks of dynamic +/// memory. + +void m61_print_leak_report() { + // Your code here. +} + + +/// m61_print_heavy_hitter_report() +/// Print a report of heavily-used allocation locations. + +void m61_print_heavy_hitter_report() { + // Your heavy-hitters code here +} diff --git a/pset1/m61.hh b/pset1/m61.hh new file mode 100644 index 0000000..d088473 --- /dev/null +++ b/pset1/m61.hh @@ -0,0 +1,95 @@ +#ifndef M61_HH +#define M61_HH 1 +#include +#include +#include +#include +#include + + +/// m61_malloc(sz, file, line) +/// Return a pointer to `sz` bytes of newly-allocated dynamic memory. +void* m61_malloc(size_t sz, const char* file, long line); + +/// m61_free(ptr, file, line) +/// Free the memory space pointed to by `ptr`. +void m61_free(void* ptr, const char* file, long line); + +/// m61_calloc(nmemb, sz, file, line) +/// Return a pointer to newly-allocated dynamic memory big enough to +/// hold an array of `nmemb` elements of `sz` bytes each. The memory +/// should be initialized to zero. +void* m61_calloc(size_t nmemb, size_t sz, const char* file, long line); + + +/// m61_statistics +/// Structure tracking memory statistics. +struct m61_statistics { + unsigned long long nactive; // # active allocations + unsigned long long active_size; // # bytes in active allocations + unsigned long long ntotal; // # total allocations + unsigned long long total_size; // # bytes in total allocations + unsigned long long nfail; // # failed allocation attempts + unsigned long long fail_size; // # bytes in failed alloc attempts + uintptr_t heap_min; // smallest allocated addr + uintptr_t heap_max; // largest allocated addr +}; + +/// m61_get_statistics(stats) +/// Store the current memory statistics in `*stats`. +void m61_get_statistics(m61_statistics* stats); + +/// m61_print_statistics() +/// Print the current memory statistics. +void m61_print_statistics(); + +/// m61_print_leak_report() +/// Print a report of all currently-active allocated blocks of dynamic +/// memory. +void m61_print_leak_report(); + +/// m61_print_heavy_hitter_report() +/// Print a report of heavily-used allocation locations. +void m61_print_heavy_hitter_report(); + +/// `m61.cc` should use these functions rather than malloc() and free(). +void* base_malloc(size_t sz); +void base_free(void* ptr); +void base_allocator_disable(bool is_disabled); + + +/// Override system versions with our versions. +#if !M61_DISABLE +#define malloc(sz) m61_malloc((sz), __FILE__, __LINE__) +#define free(ptr) m61_free((ptr), __FILE__, __LINE__) +#define calloc(nmemb, sz) m61_calloc((nmemb), (sz), __FILE__, __LINE__) +#endif + + +/// This magic class lets standard C++ containers use your debugging allocator, +/// instead of the system allocator. +template +class m61_allocator { +public: + using value_type = T; + m61_allocator() noexcept = default; + m61_allocator(const m61_allocator&) noexcept = default; + template m61_allocator(m61_allocator&) noexcept {} + + T* allocate(size_t n) { + return reinterpret_cast(m61_malloc(n * sizeof(T), "?", 0)); + } + void deallocate(T* ptr, size_t) { + m61_free(ptr, "?", 0); + } +}; +template +inline constexpr bool operator==(const m61_allocator&, const m61_allocator&) { + return true; +} +template +inline constexpr bool operator!=(const m61_allocator&, const m61_allocator&) { + return false; +} + +#endif diff --git a/pset1/test001.cc b/pset1/test001.cc new file mode 100644 index 0000000..d8219ca --- /dev/null +++ b/pset1/test001.cc @@ -0,0 +1,12 @@ +#include "m61.hh" +#include +// Trivial check: no allocations == zero statistics. + +int main() { + m61_print_statistics(); +} + +// Lines starting with "//!" define the expected output for this test. + +//! alloc count: active 0 total 0 fail 0 +//! alloc size: active 0 total 0 fail 0 diff --git a/pset1/test002.cc b/pset1/test002.cc new file mode 100644 index 0000000..e2e9539 --- /dev/null +++ b/pset1/test002.cc @@ -0,0 +1,15 @@ +#include "m61.hh" +#include +// Total allocation counts. + +int main() { + for (int i = 0; i != 10; ++i) { + (void) malloc(1); + } + m61_print_statistics(); +} + +// In expected output, "???" can match any number of characters. + +//! alloc count: active ??? total 10 fail ??? +//! alloc size: active ??? total ??? fail ??? diff --git a/pset1/test003.cc b/pset1/test003.cc new file mode 100644 index 0000000..aa27d2c --- /dev/null +++ b/pset1/test003.cc @@ -0,0 +1,17 @@ +#include "m61.hh" +#include +// Active allocation counts. + +int main() { + void* ptrs[10]; + for (int i = 0; i != 10; ++i) { + ptrs[i] = malloc(i + 1); + } + for (int i = 0; i != 5; ++i) { + free(ptrs[i]); + } + m61_print_statistics(); +} + +//! alloc count: active 5 total 10 fail ??? +//! alloc size: active ??? total ??? fail ??? diff --git a/pset1/test004.cc b/pset1/test004.cc new file mode 100644 index 0000000..1313b82 --- /dev/null +++ b/pset1/test004.cc @@ -0,0 +1,17 @@ +#include "m61.hh" +#include +// Total allocation sizes. + +int main() { + void* ptrs[10]; + for (int i = 0; i != 10; ++i) { + ptrs[i] = malloc(i + 1); + } + for (int i = 0; i != 5; ++i) { + free(ptrs[i]); + } + m61_print_statistics(); +} + +//! alloc count: active 5 total 10 fail ??? +//! alloc size: active ??? total 55 fail ??? diff --git a/pset1/test005.cc b/pset1/test005.cc new file mode 100644 index 0000000..ca59ad3 --- /dev/null +++ b/pset1/test005.cc @@ -0,0 +1,34 @@ +#include "m61.hh" +#include +#include +#include +// Failed allocation. + +int main() { + void* ptrs[10]; + for (int i = 0; i != 10; ++i) { + ptrs[i] = malloc(i + 1); + } + for (int i = 0; i != 5; ++i) { + free(ptrs[i]); + } + size_t very_large_size = (size_t) -1 - 150; + void* garbage = malloc(very_large_size); + assert(!garbage); + m61_print_statistics(); +} + +// The text within ??{...}?? pairs is a REGULAR EXPRESSION. +// (Some sites about regular expressions: +// http://www.lornajane.net/posts/2011/simple-regular-expressions-by-example +// https://www.icewarp.com/support/online_help/203030104.htm +// http://xkcd.com/208/ +// Dig deeper into how regular expresisons are implemented: +// http://swtch.com/~rsc/regexp/regexp1.html ) +// This particular regular expression lets our check work correctly on both +// 32-bit and 64-bit architectures. It checks for a `fail_size` of either +// 2^32 - 151 or 2^64 - 151. + +//! ??? +//! alloc count: active 5 total 10 fail 1 +//! alloc size: active ??? total 55 fail ??{4294967145|18446744073709551465}?? diff --git a/pset1/test006.cc b/pset1/test006.cc new file mode 100644 index 0000000..3ed314a --- /dev/null +++ b/pset1/test006.cc @@ -0,0 +1,17 @@ +#include "m61.hh" +#include +// Active allocation sizes. + +int main() { + void* ptrs[10]; + for (int i = 0; i != 10; ++i) { + ptrs[i] = malloc(i + 1); + } + for (int i = 0; i != 5; ++i) { + free(ptrs[i]); + } + m61_print_statistics(); +} + +//! alloc count: active 5 total 10 fail ??? +//! alloc size: active 40 total 55 fail ??? diff --git a/pset1/test007.cc b/pset1/test007.cc new file mode 100644 index 0000000..1b97628 --- /dev/null +++ b/pset1/test007.cc @@ -0,0 +1,16 @@ +#include "m61.hh" +#include +#include +#include +#include +// Check alignment of returned data. + +int main() { + double* ptr = (double*) malloc(sizeof(double)); + assert((uintptr_t) ptr % alignof(double) == 0); + assert((uintptr_t) ptr % alignof(unsigned long long) == 0); + + char* ptr2 = (char*) malloc(1); + assert((uintptr_t) ptr2 % alignof(double) == 0); + assert((uintptr_t) ptr2 % alignof(unsigned long long) == 0); +} diff --git a/pset1/test008.cc b/pset1/test008.cc new file mode 100644 index 0000000..61baf4e --- /dev/null +++ b/pset1/test008.cc @@ -0,0 +1,15 @@ +#include "m61.hh" +#include +#include +#include +// Null pointers are freeable. + +int main() { + void* p = malloc(10); + free(nullptr); + free(p); + m61_print_statistics(); +} + +//! alloc count: active 0 total 1 fail 0 +//! alloc size: active 0 total 10 fail 0 diff --git a/pset1/test009.cc b/pset1/test009.cc new file mode 100644 index 0000000..f976bef --- /dev/null +++ b/pset1/test009.cc @@ -0,0 +1,13 @@ +#include "m61.hh" +#include +#include +#include +// heap_min and heap_max checking, simple case. + +int main() { + char* p = (char*) malloc(10); + m61_statistics stat; + m61_get_statistics(&stat); + assert((uintptr_t) p >= stat.heap_min); + assert((uintptr_t) p + 10 <= stat.heap_max); +} diff --git a/pset1/test010.cc b/pset1/test010.cc new file mode 100644 index 0000000..41ca9ed --- /dev/null +++ b/pset1/test010.cc @@ -0,0 +1,26 @@ +#include "m61.hh" +#include +#include +#include +#include +// heap_min and heap_max checking, more allocations. + +int main() { + uintptr_t heap_min = 0; + uintptr_t heap_max = 0; + for (int i = 0; i != 100; ++i) { + size_t sz = rand() % 100; + char* p = (char*) malloc(sz); + if (!heap_min || heap_min > (uintptr_t) p) { + heap_min = (uintptr_t) p; + } + if (!heap_max || heap_max < (uintptr_t) p + sz) { + heap_max = (uintptr_t) p + sz; + } + free(p); + } + m61_statistics stat; + m61_get_statistics(&stat); + assert(heap_min >= stat.heap_min); + assert(heap_max <= stat.heap_max); +} diff --git a/pset1/test011.cc b/pset1/test011.cc new file mode 100644 index 0000000..b299a7c --- /dev/null +++ b/pset1/test011.cc @@ -0,0 +1,31 @@ +#include "m61.hh" +#include +#include +#include +#include +// heap_min and heap_max checking, no overlap with other regions. + +static int global; + +int main() { + for (int i = 0; i != 100; ++i) { + size_t sz = rand() % 100; + char* p = (char*) malloc(sz); + free(p); + } + m61_statistics stat; + m61_get_statistics(&stat); + + union { + uintptr_t addr; + int* iptr; + m61_statistics* statptr; + int (*mainptr)(); + } x; + x.iptr = &global; + assert(x.addr + sizeof(int) < stat.heap_min || x.addr >= stat.heap_max); + x.statptr = &stat; + assert(x.addr + sizeof(int) < stat.heap_min || x.addr >= stat.heap_max); + x.mainptr = &main; + assert(x.addr + sizeof(int) < stat.heap_min || x.addr >= stat.heap_max); +} diff --git a/pset1/test012.cc b/pset1/test012.cc new file mode 100644 index 0000000..03dd5f0 --- /dev/null +++ b/pset1/test012.cc @@ -0,0 +1,21 @@ +#include "m61.hh" +#include +#include +// Diabolical failed allocation. + +int main() { + void* ptrs[10]; + for (int i = 0; i != 10; ++i) { + ptrs[i] = malloc(i + 1); + } + for (int i = 0; i != 5; ++i) { + free(ptrs[i]); + } + size_t very_large_size = (size_t) -1; + void* garbage = malloc(very_large_size); + assert(!garbage); + m61_print_statistics(); +} + +//! alloc count: active 5 total 10 fail 1 +//! alloc size: active 40 total 55 fail ??{4294967295|18446744073709551615}?? diff --git a/pset1/test013.cc b/pset1/test013.cc new file mode 100644 index 0000000..2538779 --- /dev/null +++ b/pset1/test013.cc @@ -0,0 +1,17 @@ +#include "m61.hh" +#include +#include +#include +// Calloc. + +const char data[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +int main() { + char* p = (char*) calloc(10, 1); + assert(p != nullptr); + assert(memcmp(data, p, 10) == 0); + m61_print_statistics(); +} + +//! alloc count: active 1 total 1 fail 0 +//! alloc size: active 10 total 10 fail 0 diff --git a/pset1/test014.cc b/pset1/test014.cc new file mode 100644 index 0000000..63b0900 --- /dev/null +++ b/pset1/test014.cc @@ -0,0 +1,15 @@ +#include "m61.hh" +#include +#include +#include +// Diabolical calloc. + +int main() { + size_t very_large_nmemb = (size_t) -1 / 8 + 2; + void* p = calloc(very_large_nmemb, 16); + assert(p == nullptr); + m61_print_statistics(); +} + +//! alloc count: active 0 total 0 fail 1 +//! alloc size: active 0 total 0 fail ??? diff --git a/pset1/test015.cc b/pset1/test015.cc new file mode 100644 index 0000000..aed29f0 --- /dev/null +++ b/pset1/test015.cc @@ -0,0 +1,18 @@ +#include "m61.hh" +#include +#include +#include +// Another nasty calloc. + +struct large_struct { + short a[0x80000001UL]; +}; + +int main() { + void* p = calloc(0x100000001UL, sizeof(large_struct)); + assert(p == nullptr); + m61_print_statistics(); +} + +//! alloc count: active 0 total 0 fail 1 +//! alloc size: active 0 total 0 fail ??? diff --git a/pset1/test016.cc b/pset1/test016.cc new file mode 100644 index 0000000..da1edaf --- /dev/null +++ b/pset1/test016.cc @@ -0,0 +1,13 @@ +#include "m61.hh" +#include +#include +#include +// Free of invalid pointer. + +int main() { + free((void*) 16); + m61_print_statistics(); +} + +//! MEMORY BUG???: invalid free of pointer ???, not in heap +//! ??? diff --git a/pset1/test017.cc b/pset1/test017.cc new file mode 100644 index 0000000..71cfdd5 --- /dev/null +++ b/pset1/test017.cc @@ -0,0 +1,14 @@ +#include "m61.hh" +#include +#include +#include +// Free of invalid pointer. + +int main() { + char* ptr = (char*) malloc(32); + free(ptr - 32); + m61_print_statistics(); +} + +//! MEMORY BUG???: invalid free of pointer ???, not in heap +//! ??? diff --git a/pset1/test018.cc b/pset1/test018.cc new file mode 100644 index 0000000..733cda1 --- /dev/null +++ b/pset1/test018.cc @@ -0,0 +1,20 @@ +#include "m61.hh" +#include +#include +#include +// Free of invalid pointer after some successful allocations. + +int main() { + void* ptrs[10]; + for (int i = 0; i != 10; ++i) { + ptrs[i] = malloc(i + 1); + } + for (int i = 0; i != 5; ++i) { + free(ptrs[i]); + } + free((void*) 16); + m61_print_statistics(); +} + +//! MEMORY BUG???: invalid free of pointer ???, not in heap +//! ??? diff --git a/pset1/test019.cc b/pset1/test019.cc new file mode 100644 index 0000000..a141405 --- /dev/null +++ b/pset1/test019.cc @@ -0,0 +1,14 @@ +#include "m61.hh" +#include +#include +#include +// Wild free. + +int main() { + int x; + free(&x); + m61_print_statistics(); +} + +//! MEMORY BUG???: invalid free of pointer ???, not in heap +//! ??? diff --git a/pset1/test020.cc b/pset1/test020.cc new file mode 100644 index 0000000..5e76403 --- /dev/null +++ b/pset1/test020.cc @@ -0,0 +1,17 @@ +#include "m61.hh" +#include +#include +#include +// Double free. + +int main() { + void* ptr = malloc(2001); + fprintf(stderr, "Will free %p\n", ptr); + free(ptr); + free(ptr); + m61_print_statistics(); +} + +//! Will free ??{0x\w+}=ptr?? +//! MEMORY BUG???: invalid free of pointer ??ptr??, double free +//! ??? diff --git a/pset1/test021.cc b/pset1/test021.cc new file mode 100644 index 0000000..3c56764 --- /dev/null +++ b/pset1/test021.cc @@ -0,0 +1,16 @@ +#include "m61.hh" +#include +#include +#include +// Wild free inside heap-allocated data. + +int main() { + void* ptr = malloc(2001); + fprintf(stderr, "Bad pointer %p\n", (char*) ptr + 128); + free((char*) ptr + 128); + m61_print_statistics(); +} + +//! Bad pointer ??{0x\w+}=ptr?? +//! MEMORY BUG???: invalid free of pointer ??ptr??, not allocated +//! ??? diff --git a/pset1/test022.cc b/pset1/test022.cc new file mode 100644 index 0000000..ef35885 --- /dev/null +++ b/pset1/test022.cc @@ -0,0 +1,16 @@ +#include "m61.hh" +#include +#include +#include +// File name and line number of wild free. + +int main() { + void* ptr = malloc(2001); + fprintf(stderr, "Bad pointer %p\n", (char*) ptr + 128); + free((char*) ptr + 128); + m61_print_statistics(); +} + +//! Bad pointer ??{0x\w+}=ptr?? +//! MEMORY BUG: test???.cc:10: invalid free of pointer ??ptr??, not allocated +//! ??? diff --git a/pset1/test023.cc b/pset1/test023.cc new file mode 100644 index 0000000..4b674b6 --- /dev/null +++ b/pset1/test023.cc @@ -0,0 +1,16 @@ +#include "m61.hh" +#include +#include +#include +// Misaligned wild free. + +int main() { + void* ptr = malloc(2001); + fprintf(stderr, "Bad pointer %p\n", (char*) ptr + 127); + free((char*) ptr + 127); + m61_print_statistics(); +} + +//! Bad pointer ??{0x\w+}=ptr?? +//! MEMORY BUG: test???.cc:10: invalid free of pointer ??ptr??, not allocated +//! ??? diff --git a/pset1/test024.cc b/pset1/test024.cc new file mode 100644 index 0000000..444c5e9 --- /dev/null +++ b/pset1/test024.cc @@ -0,0 +1,19 @@ +#include "m61.hh" +#include +#include +#include +// A correct execution should not report errors. + +int main() { + for (int i = 0; i != 10; ++i) { + int* ptr = (int*) malloc(10 * sizeof(int)); + for (int j = 0; j != 10; ++j) { + ptr[i] = i; + } + free(ptr); + } + m61_print_statistics(); +} + +//! alloc count: active 0 total 10 fail 0 +//! alloc size: active 0 total 400 fail 0 diff --git a/pset1/test025.cc b/pset1/test025.cc new file mode 100644 index 0000000..c1d6ce5 --- /dev/null +++ b/pset1/test025.cc @@ -0,0 +1,19 @@ +#include "m61.hh" +#include +#include +#include +// Check for boundary write errors off the end of an allocated block. + +int main() { + int* ptr = (int*) malloc(sizeof(int) * 10); + fprintf(stderr, "Will free %p\n", ptr); + for (int i = 0; i <= 10 /* Whoops! Should be < */; ++i) { + ptr[i] = i; + } + free(ptr); + m61_print_statistics(); +} + +//! Will free ??{0x\w+}=ptr?? +//! MEMORY BUG???: detected wild write during free of pointer ??ptr?? +//! ??? diff --git a/pset1/test026.cc b/pset1/test026.cc new file mode 100644 index 0000000..53440b2 --- /dev/null +++ b/pset1/test026.cc @@ -0,0 +1,19 @@ +#include "m61.hh" +#include +#include +#include +// More boundary write error checks #1. + +int main() { + const char* string1 = "Hello, this is a string! I exist to demonstrate a common error."; + char* copy_of_string1 = (char*) malloc(strlen(string1)); + // Whoops! Forgot to allocate space for the '\0' that ends the string. + strcpy(copy_of_string1, string1); // == boundary write error + fprintf(stderr, "About to free %p\n", copy_of_string1); + free(copy_of_string1); + m61_print_statistics(); +} + +//! About to free ??{0x\w+}=ptr?? +//! MEMORY BUG???: detected wild write during free of pointer ??ptr?? +//! ??? diff --git a/pset1/test027.cc b/pset1/test027.cc new file mode 100644 index 0000000..d591131 --- /dev/null +++ b/pset1/test027.cc @@ -0,0 +1,17 @@ +#include "m61.hh" +#include +#include +#include +// More boundary write error checks #2. + +int main() { + int* array = (int*) malloc(3); // oops, forgot "* sizeof(int)" + for (int i = 0; i != 3; ++i) { + array[i] = 0; + } + free(array); + m61_print_statistics(); +} + +//! MEMORY BUG???: detected wild write during free of pointer ??? +//! ??? diff --git a/pset1/test028.cc b/pset1/test028.cc new file mode 100644 index 0000000..1765cc3 --- /dev/null +++ b/pset1/test028.cc @@ -0,0 +1,31 @@ +#include "m61.hh" +#include +#include +#include +// Memory leak report with no leaks. + +struct node { + node* next; +}; + +int main() { + node* list = nullptr; + + // create a list + for (int i = 0; i != 400; ++i) { + node* n = (node*) malloc(sizeof(node)); + n->next = list; + list = n; + } + + // free everything in it + while (node* n = list) { + list = n->next; + free(n); + } + + m61_print_leak_report(); + printf("OK\n"); +} + +//! OK diff --git a/pset1/test029.cc b/pset1/test029.cc new file mode 100644 index 0000000..0042901 --- /dev/null +++ b/pset1/test029.cc @@ -0,0 +1,38 @@ +#include "m61.hh" +#include +#include +#include +// Memory leak report with one leak. + +struct node { + node* next; +}; + +int main() { + node* list = nullptr; + + // create a list + for (int i = 0; i != 400; ++i) { + node* n = (node*) malloc(sizeof(node)); + n->next = list; + list = n; + } + + // free everything in it but one + while (list && list->next) { + node** pprev = &list; + while (node* n = *pprev) { + *pprev = n->next; + free(n); + if (*pprev) { + pprev = &(*pprev)->next; + } + } + } + + printf("EXPECTED LEAK: %p with size %zu\n", list, sizeof(node)); + m61_print_leak_report(); +} + +//! EXPECTED LEAK: ??{0x\w*}=ptr?? with size ??{\d+}=size?? +//! LEAK CHECK: test???.cc:16: allocated object ??ptr?? with size ??size?? diff --git a/pset1/test030.cc b/pset1/test030.cc new file mode 100644 index 0000000..304e4cb --- /dev/null +++ b/pset1/test030.cc @@ -0,0 +1,36 @@ +#include "m61.hh" +#include +#include +#include +// Memory leak report with multiple leaks. + +int main() { + char* ptrs[10]; + ptrs[0] = (char*) malloc(10); + ptrs[1] = (char*) malloc(11); + ptrs[2] = (char*) malloc(12); + ptrs[3] = (char*) malloc(13); + ptrs[4] = (char*) malloc(14); + ptrs[5] = (char*) malloc(15); + ptrs[6] = (char*) malloc(16); + ptrs[7] = (char*) malloc(17); + ptrs[8] = (char*) malloc(18); + ptrs[9] = (char*) malloc(19); + + free(ptrs[3]); + free(ptrs[8]); + free(ptrs[0]); + m61_print_leak_report(); +} + +// The "//!!UNORDERED" line tells the check.pl script to allow the +// expected lines to occur in any order. + +//!!UNORDERED +//! LEAK CHECK: test???.cc:10: allocated object ??{\w+}?? with size 11 +//! LEAK CHECK: test???.cc:11: allocated object ??{\w+}?? with size 12 +//! LEAK CHECK: test???.cc:13: allocated object ??{\w+}?? with size 14 +//! LEAK CHECK: test???.cc:14: allocated object ??{\w+}?? with size 15 +//! LEAK CHECK: test???.cc:15: allocated object ??{\w+}?? with size 16 +//! LEAK CHECK: test???.cc:16: allocated object ??{\w+}?? with size 17 +//! LEAK CHECK: test???.cc:18: allocated object ??{\w+}?? with size 19 diff --git a/pset1/test031.cc b/pset1/test031.cc new file mode 100644 index 0000000..0a5ee10 --- /dev/null +++ b/pset1/test031.cc @@ -0,0 +1,15 @@ +#include "m61.hh" +#include +#include +#include +// Advanced error message for freeing data inside another heap block. + +int main() { + void* ptr = malloc(2001); + free((char*) ptr + 128); + m61_print_statistics(); +} + +//! MEMORY BUG: test???.cc:9: invalid free of pointer ???, not allocated +//! test???.cc:8: ??? is 128 bytes inside a 2001 byte region allocated here +//! ??? diff --git a/pset1/test032.cc b/pset1/test032.cc new file mode 100644 index 0000000..ff7bdd1 --- /dev/null +++ b/pset1/test032.cc @@ -0,0 +1,19 @@ +#include "m61.hh" +#include +#include +#include +// Diabolical wild free #1. + +int main() { + char* a = (char*) malloc(200); + char* b = (char*) malloc(50); + char* c = (char*) malloc(200); + char* p = (char*) malloc(3000); + (void) a, (void) c; + memcpy(p, b - 200, 450); + free(p + 200); + m61_print_statistics(); +} + +//! MEMORY BUG???: invalid free of pointer ???, not allocated +//! ??? diff --git a/pset1/test033.cc b/pset1/test033.cc new file mode 100644 index 0000000..b7e790e --- /dev/null +++ b/pset1/test033.cc @@ -0,0 +1,21 @@ +#include "m61.hh" +#include +#include +#include +// Diabolical wild free #2. + +int main() { + char* a = (char*) malloc(200); + char* b = (char*) malloc(50); + char* c = (char*) malloc(200); + char* p = (char*) malloc(3000); + (void) a, (void) c; + memcpy(p, b - 200, 450); + free(b); + memcpy(b - 200, p, 450); + free(b); + m61_print_statistics(); +} + +//! MEMORY BUG???: ??? free of pointer ??? +//! ??? diff --git a/pset1/test034.cc b/pset1/test034.cc new file mode 100644 index 0000000..06420df --- /dev/null +++ b/pset1/test034.cc @@ -0,0 +1,85 @@ +#include "m61.hh" +#include +#include +#include +// Exercise large numbers of allocation sites. + +const char* files[200] = { + "unprovoked.cc", "perissodactylate.cc", "makhzan.cc", "Janthinidae.cc", + "allothigenetic.cc", "taller.cc", "salmis.cc", "fortuity.cc", + "monarchically.cc", "cringe.cc", "wolfen.cc", "seem.cc", "pneumological.cc", + "Mysis.cc", "tumulus.cc", "rhombohedral.cc", "Gold.cc", "nosographical.cc", + "choice.cc", "quinible.cc", "reintuitive.cc", "unprimed.cc", "hexapla.cc", + "Salish.cc", "lush.cc", "ignifuge.cc", "bemercy.cc", "Bolelia.cc", + "thoracobronchotomy.cc", "viewster.cc", "assumption.cc", "travail.cc", + "progospel.cc", "undropsical.cc", "forjesket.cc", "astrolithology.cc", + "rooklet.cc", "animalcule.cc", "pulpotomy.cc", "debullition.cc", + "intransferable.cc", "subterrestrial.cc", "dosimeter.cc", + "straightforwardness.cc", "unlaundered.cc", "ornamentalize.cc", + "creatorhood.cc", "urbify.cc", "willies.cc", "ergotic.cc", "demoralization.cc", + "unpermeable.cc", "sensuousness.cc", "acetation.cc", "scart.cc", "rubican.cc", + "wronged.cc", "deficience.cc", "libretti.cc", "Brownistic.cc", "zaratite.cc", + "applesauce.cc", "vetanda.cc", "Luganda.cc", "zoophysiology.cc", + "epiplectic.cc", "obstructer.cc", "birddom.cc", "cerebral.cc", + "Melonechinus.cc", "blennothorax.cc", "ramule.cc", "phenolate.cc", + "phytography.cc", "Esdras.cc", "incruental.cc", "archichlamydeous.cc", + "Marginella.cc", "foreconceive.cc", "passionful.cc", "analcimite.cc", + "dharma.cc", "scoleciasis.cc", "scentless.cc", "cliff.cc", "sprawly.cc", + "oleraceous.cc", "pentanedione.cc", "cendre.cc", "autoreinfusion.cc", + "ternize.cc", "supraclavicle.cc", "circumstance.cc", "diphtheritic.cc", + "Didache.cc", "preseminal.cc", "scoldable.cc", "cicisbeism.cc", "schapping.cc", + "anterior.cc", "ryme.cc", "turion.cc", "boattail.cc", "Artamidae.cc", + "healder.cc", "portside.cc", "domesticative.cc", "tempered.cc", + "vaticinate.cc", "Electra.cc", "nectarize.cc", "inturn.cc", "murgeon.cc", + "exhaustlessly.cc", "holoclastic.cc", "linkboy.cc", "moiety.cc", + "bletheration.cc", "chartometer.cc", "laughful.cc", "eccentrate.cc", + "erythrosinophile.cc", "immetricalness.cc", "tangently.cc", "Jeffersonia.cc", + "subphratry.cc", "dermatotome.cc", "footscald.cc", "myrialitre.cc", + "amiced.cc", "jaspilite.cc", "butyrolactone.cc", "Shiraz.cc", "misalter.cc", + "Teutonicism.cc", "ungroundable.cc", "phosphuria.cc", "salmonet.cc", + "resupinate.cc", "Edoni.cc", "jewel.cc", "mysteriosophy.cc", "tolunitrile.cc", + "Cantabri.cc", "schizognathous.cc", "apotypic.cc", "Chanca.cc", + "conversibility.cc", "ergastoplasmic.cc", "biweekly.cc", "Zuni.cc", + "lignitize.cc", "sharklike.cc", "Stercorariidae.cc", "angiology.cc", + "Ostyak.cc", "Pakhpuluk.cc", "nocake.cc", "Pullman.cc", "orb.cc", "yardland.cc", + "Bahmani.cc", "stagnation.cc", "hidling.cc", "seasickness.cc", "underpan.cc", + "colophonium.cc", "underplan.cc", "extraparental.cc", "steatomatous.cc", + "rosette.cc", "circumspectively.cc", "Kevyn.cc", "adeem.cc", "eighthly.cc", + "laundryman.cc", "cellipetal.cc", "floorless.cc", "outthruster.cc", + "pawpaw.cc", "nubile.cc", "radiocaster.cc", "microcolorimetric.cc", + "unthrivingly.cc", "ribless.cc", "Phylloxeridae.cc", "Housatonic.cc", + "rebegin.cc", "queenship.cc", "pobby.cc", "deconcentrate.cc", + "Gasteromycetes.cc", "prebestow.cc", "printableness.cc", "photozincotypy.cc", + "unamenable.cc", "fawnery.cc", "radiodetector.cc", "hemoclasis.cc", + "epepophysis.cc" +}; + +int main() { + // array of pointers + const int nptrs = 200; + void* ptrs[nptrs]; + for (int i = 0; i != nptrs; ++i) { + ptrs[i] = nullptr; + } + + // do 1M allocations from different apparent allocation sites + for (unsigned i = 0; i != 500000; ++i) { + const char* file = files[random() % 200]; + int line = 1 + random() % 200; + void* ptr = m61_malloc(1 + random() % 128, file, line); + + int slot = random() % nptrs; + m61_free(ptrs[slot], file, line + 3); + ptrs[slot] = ptr; + } + + for (int i = 0; i != nptrs; ++i) { + free(ptrs[i]); + } + + m61_print_statistics(); +} + +//!!TIME +//! alloc count: active 0 total 500000 fail 0 +//! alloc size: active 0 total ??? fail ??? diff --git a/pset1/test035.cc b/pset1/test035.cc new file mode 100644 index 0000000..4a48246 --- /dev/null +++ b/pset1/test035.cc @@ -0,0 +1,19 @@ +#include "m61.hh" +#include +#include +#include +// Now C++ library functions call your allocator +// (but do not provide line number information). + +int main() { + // The `m61_allocator` argument tells the C++ standard library + // to allocate `v`’s memory using `m61_malloc/free`. + std::vector> v; + for (int i = 0; i != 100; ++i) { + v.push_back(i); + } + m61_print_statistics(); +} + +//! alloc count: active 1 total ??>=1?? fail 0 +//! alloc size: active ??>=400?? total ??>=400?? fail ??? diff --git a/pset1/test036.cc b/pset1/test036.cc new file mode 100644 index 0000000..632a4a7 --- /dev/null +++ b/pset1/test036.cc @@ -0,0 +1,22 @@ +#include "m61.hh" +#include +#include +#include +// Demonstrate destructors. + +void f() { + std::vector> v; + for (int i = 0; i != 100; ++i) { + v.push_back(i); + } + // `v` has automatic lifetime, so it is destroyed here. +} + +int main() { + f(); + m61_print_statistics(); + m61_print_leak_report(); +} + +//! alloc count: active 0 total ??>=1?? fail 0 +//! alloc size: active 0 total ??>=400?? fail 0 diff --git a/pset1/test037.cc b/pset1/test037.cc new file mode 100644 index 0000000..8c155c3 --- /dev/null +++ b/pset1/test037.cc @@ -0,0 +1,36 @@ +#include "m61.hh" +#include + +char* ptrs[100]; + +void test(int nalloc, int nfree) { + m61_allocator allocator; + for (int i = 0; i < nalloc - 1; ++i) { + ptrs[i] = allocator.allocate(i + 1); + } + if (nalloc > 0) { + ptrs[nalloc - 1] = allocator.allocate(nalloc); + } + for (int i = 0; i != nfree; ++i) { + allocator.deallocate(ptrs[i], i + 1); + } +} + +int main(int argc, char** argv) { + int nalloc = argc < 2 ? 10 : strtol(argv[1], nullptr, 0); + int nfree = argc < 3 ? 5 : strtol(argv[2], nullptr, 0); + assert(nalloc >= 0 && nfree >= 0); + assert(nalloc <= 100 && nfree <= nalloc); + test(nalloc, nfree); + m61_print_statistics(); + m61_print_leak_report(); +} + +//!!UNORDERED +//! alloc count: active 5 total 10 fail ??? +//! alloc size: active 40 total 55 fail ??? +//! LEAK CHECK: ???: allocated object ??{\w+}?? with size 6 +//! LEAK CHECK: ???: allocated object ??{\w+}?? with size 7 +//! LEAK CHECK: ???: allocated object ??{\w+}?? with size 8 +//! LEAK CHECK: ???: allocated object ??{\w+}?? with size 9 +//! LEAK CHECK: ???: allocated object ??{\w+}?? with size 10 diff --git a/pset1/test038.cc b/pset1/test038.cc new file mode 100644 index 0000000..dc7a8f6 --- /dev/null +++ b/pset1/test038.cc @@ -0,0 +1,27 @@ +#include "m61.hh" +#include +#include +#include +#include +// heap_min and heap_max checking, more allocations. + +int main() { + uintptr_t heap_min = 0; + uintptr_t heap_max = 0; + m61_allocator allocator; + for (int i = 0; i != 100; ++i) { + size_t sz = rand() % 100; + char* p = allocator.allocate(sz); + if (!heap_min || heap_min > (uintptr_t) p) { + heap_min = (uintptr_t) p; + } + if (!heap_max || heap_max < (uintptr_t) p + sz) { + heap_max = (uintptr_t) p + sz; + } + allocator.deallocate(p, sz); + } + m61_statistics stat; + m61_get_statistics(&stat); + assert(heap_min >= stat.heap_min); + assert(heap_max <= stat.heap_max); +} diff --git a/pset1/test039.cc b/pset1/test039.cc new file mode 100644 index 0000000..e989d10 --- /dev/null +++ b/pset1/test039.cc @@ -0,0 +1,31 @@ +#include "m61.hh" +#include +#include +#include +// Wild free inside heap-allocated data. + +struct whatever { + int first[100]; + char second[3000]; + int third[200]; +}; + +int main(int argc, char** argv) { + m61_allocator allocator; + // “allocate space for one `whatever` object” + whatever* object = allocator.allocate(1); + + uintptr_t addr = reinterpret_cast(object); + if (argc < 2) { + addr += 3000; + } else { + addr += strtol(argv[1], nullptr, 0); + } + + whatever* trick = reinterpret_cast(addr); + allocator.deallocate(trick, 1); + m61_print_statistics(); +} + +//! MEMORY BUG???: invalid free of pointer ???, not allocated +//! ???