/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/Makefile |
---|
0,0 → 1,40 |
# |
# Students' Makefile for the Malloc Lab |
# |
VERSION = 1 |
CC = gcc |
CFLAGS = -Wall -O3 -Werror -m32 |
# for debugging |
# CFLAGS = -Wall -g -Werror -m32 |
SHARED_OBJS = mdriver.o memlib.o fsecs.o fcyc.o clock.o ftimer.o list.o |
OBJS = $(SHARED_OBJS) mm.o |
BOOK_IMPL_OBJS = $(SHARED_OBJS) mm-book-implicit.o |
GBACK_IMPL_OBJS = $(SHARED_OBJS) mm-gback-implicit.o |
mdriver: $(OBJS) |
$(CC) $(CFLAGS) -o mdriver $(OBJS) |
mdriver-book: $(BOOK_IMPL_OBJS) |
$(CC) $(CFLAGS) -o $@ $(BOOK_IMPL_OBJS) |
mdriver-gback: $(GBACK_IMPL_OBJS) |
$(CC) $(CFLAGS) -o $@ $(GBACK_IMPL_OBJS) |
mdriver.o: mdriver.c fsecs.h fcyc.h clock.h memlib.h config.h mm.h |
memlib.o: memlib.c memlib.h |
mm.o: mm.c mm.h memlib.h |
fsecs.o: fsecs.c fsecs.h config.h |
fcyc.o: fcyc.c fcyc.h |
ftimer.o: ftimer.c ftimer.h config.h |
clock.o: clock.c clock.h |
list.o: list.c list.h |
handin: |
/home/courses/cs3214/bin/submit.pl p4 mm.c |
clean: |
rm -f *~ *.o mdriver |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/README |
---|
0,0 → 1,53 |
##################################################################### |
# CS:APP Malloc Lab |
# Handout files for students |
# |
# Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. |
# May not be used, modified, or copied without permission. |
# |
###################################################################### |
*********** |
Main Files: |
*********** |
mm.{c,h} |
Your solution malloc package. mm.c is the file that you |
will be handing in, and is the only file you should modify. |
mdriver.c |
The malloc driver that tests your mm.c file |
short{1,2}-bal.rep |
Two tiny tracefiles to help you get started. |
Makefile |
Builds the driver |
********************************** |
Other support files for the driver |
********************************** |
config.h Configures the malloc lab driver |
fsecs.{c,h} Wrapper function for the different timer packages |
clock.{c,h} Routines for accessing the Pentium and Alpha cycle counters |
fcyc.{c,h} Timer functions based on cycle counters |
ftimer.{c,h} Timer functions based on interval timers and gettimeofday() |
memlib.{c,h} Models the heap and sbrk function |
list.{c,h} A doubly-linked list implementation you are free to use |
******************************* |
Building and running the driver |
******************************* |
To build the driver, type "make" to the shell. |
To run the driver on a tiny test trace: |
unix> mdriver -V -f short1-bal.rep |
The -V option prints out helpful tracing and summary information. |
To get a list of the driver flags: |
unix> mdriver -h |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/clock.c |
---|
0,0 → 1,279 |
/* |
* clock.c - Routines for using the cycle counters on x86, |
* Alpha, and Sparc boxes. |
* |
* Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. |
* May not be used, modified, or copied without permission. |
*/ |
#include <stdio.h> |
#include <stdlib.h> |
#include <unistd.h> |
#include <sys/times.h> |
#include "clock.h" |
/******************************************************* |
* Machine dependent functions |
* |
* Note: the constants __i386__ and __alpha |
* are set by GCC when it calls the C preprocessor |
* You can verify this for yourself using gcc -v. |
*******************************************************/ |
#if defined(__i386__) |
/******************************************************* |
* Pentium versions of start_counter() and get_counter() |
*******************************************************/ |
/* $begin x86cyclecounter */ |
/* Initialize the cycle counter */ |
static unsigned cyc_hi = 0; |
static unsigned cyc_lo = 0; |
/* Set *hi and *lo to the high and low order bits of the cycle counter. |
Implementation requires assembly code to use the rdtsc instruction. */ |
void access_counter(unsigned *hi, unsigned *lo) |
{ |
asm volatile ("rdtsc; movl %%edx,%0; movl %%eax,%1" /* Read cycle counter */ |
: "=r" (*hi), "=r" (*lo) /* and move results to */ |
: /* No input */ /* the two outputs */ |
: "%edx", "%eax"); |
} |
/* Record the current value of the cycle counter. */ |
void start_counter() |
{ |
access_counter(&cyc_hi, &cyc_lo); |
} |
/* Return the number of cycles since the last call to start_counter. */ |
double get_counter() |
{ |
unsigned ncyc_hi, ncyc_lo; |
unsigned hi, lo, borrow; |
double result; |
/* Get cycle counter */ |
access_counter(&ncyc_hi, &ncyc_lo); |
/* Do double precision subtraction */ |
lo = ncyc_lo - cyc_lo; |
borrow = lo > ncyc_lo; |
hi = ncyc_hi - cyc_hi - borrow; |
result = (double) hi * (1 << 30) * 4 + lo; |
if (result < 0) { |
fprintf(stderr, "Error: counter returns neg value: %.0f\n", result); |
} |
return result; |
} |
/* $end x86cyclecounter */ |
#elif defined(__alpha) |
/**************************************************** |
* Alpha versions of start_counter() and get_counter() |
***************************************************/ |
/* Initialize the cycle counter */ |
static unsigned cyc_hi = 0; |
static unsigned cyc_lo = 0; |
/* Use Alpha cycle timer to compute cycles. Then use |
measured clock speed to compute seconds |
*/ |
/* |
* counterRoutine is an array of Alpha instructions to access |
* the Alpha's processor cycle counter. It uses the rpcc |
* instruction to access the counter. This 64 bit register is |
* divided into two parts. The lower 32 bits are the cycles |
* used by the current process. The upper 32 bits are wall |
* clock cycles. These instructions read the counter, and |
* convert the lower 32 bits into an unsigned int - this is the |
* user space counter value. |
* NOTE: The counter has a very limited time span. With a |
* 450MhZ clock the counter can time things for about 9 |
* seconds. */ |
static unsigned int counterRoutine[] = |
{ |
0x601fc000u, |
0x401f0000u, |
0x6bfa8001u |
}; |
/* Cast the above instructions into a function. */ |
static unsigned int (*counter)(void)= (void *)counterRoutine; |
void start_counter() |
{ |
/* Get cycle counter */ |
cyc_hi = 0; |
cyc_lo = counter(); |
} |
double get_counter() |
{ |
unsigned ncyc_hi, ncyc_lo; |
unsigned hi, lo, borrow; |
double result; |
ncyc_lo = counter(); |
ncyc_hi = 0; |
lo = ncyc_lo - cyc_lo; |
borrow = lo > ncyc_lo; |
hi = ncyc_hi - cyc_hi - borrow; |
result = (double) hi * (1 << 30) * 4 + lo; |
if (result < 0) { |
fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); |
} |
return result; |
} |
#else |
/**************************************************************** |
* All the other platforms for which we haven't implemented cycle |
* counter routines. Newer models of sparcs (v8plus) have cycle |
* counters that can be accessed from user programs, but since there |
* are still many sparc boxes out there that don't support this, we |
* haven't provided a Sparc version here. |
***************************************************************/ |
void start_counter() |
{ |
printf("ERROR: You are trying to use a start_counter routine in clock.c\n"); |
printf("that has not been implemented yet on this platform.\n"); |
printf("Please choose another timing package in config.h.\n"); |
exit(1); |
} |
double get_counter() |
{ |
printf("ERROR: You are trying to use a get_counter routine in clock.c\n"); |
printf("that has not been implemented yet on this platform.\n"); |
printf("Please choose another timing package in config.h.\n"); |
exit(1); |
} |
#endif |
/******************************* |
* Machine-independent functions |
******************************/ |
double ovhd() |
{ |
/* Do it twice to eliminate cache effects */ |
int i; |
double result; |
for (i = 0; i < 2; i++) { |
start_counter(); |
result = get_counter(); |
} |
return result; |
} |
/* $begin mhz */ |
/* Estimate the clock rate by measuring the cycles that elapse */ |
/* while sleeping for sleeptime seconds */ |
double mhz_full(int verbose, int sleeptime) |
{ |
double rate; |
start_counter(); |
sleep(sleeptime); |
rate = get_counter() / (1e6*sleeptime); |
if (verbose) |
printf("Processor clock rate ~= %.1f MHz\n", rate); |
return rate; |
} |
/* $end mhz */ |
/* Version using a default sleeptime */ |
double mhz(int verbose) |
{ |
return mhz_full(verbose, 2); |
} |
/** Special counters that compensate for timer interrupt overhead */ |
static double cyc_per_tick = 0.0; |
#define NEVENT 100 |
#define THRESHOLD 1000 |
#define RECORDTHRESH 3000 |
/* Attempt to see how much time is used by timer interrupt */ |
static void callibrate(int verbose) |
{ |
double oldt; |
struct tms t; |
clock_t oldc; |
int e = 0; |
times(&t); |
oldc = t.tms_utime; |
start_counter(); |
oldt = get_counter(); |
while (e <NEVENT) { |
double newt = get_counter(); |
if (newt-oldt >= THRESHOLD) { |
clock_t newc; |
times(&t); |
newc = t.tms_utime; |
if (newc > oldc) { |
double cpt = (newt-oldt)/(newc-oldc); |
if ((cyc_per_tick == 0.0 || cyc_per_tick > cpt) && cpt > RECORDTHRESH) |
cyc_per_tick = cpt; |
/* |
if (verbose) |
printf("Saw event lasting %.0f cycles and %d ticks. Ratio = %f\n", |
newt-oldt, (int) (newc-oldc), cpt); |
*/ |
e++; |
oldc = newc; |
} |
oldt = newt; |
} |
} |
if (verbose) |
printf("Setting cyc_per_tick to %f\n", cyc_per_tick); |
} |
static clock_t start_tick = 0; |
void start_comp_counter() |
{ |
struct tms t; |
if (cyc_per_tick == 0.0) |
callibrate(0); |
times(&t); |
start_tick = t.tms_utime; |
start_counter(); |
} |
double get_comp_counter() |
{ |
double time = get_counter(); |
double ctime; |
struct tms t; |
clock_t ticks; |
times(&t); |
ticks = t.tms_utime - start_tick; |
ctime = time - ticks*cyc_per_tick; |
/* |
printf("Measured %.0f cycles. Ticks = %d. Corrected %.0f cycles\n", |
time, (int) ticks, ctime); |
*/ |
return ctime; |
} |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/clock.h |
---|
0,0 → 1,22 |
/* Routines for using cycle counter */ |
/* Start the counter */ |
void start_counter(); |
/* Get # cycles since counter started */ |
double get_counter(); |
/* Measure overhead for counter */ |
double ovhd(); |
/* Determine clock rate of processor (using a default sleeptime) */ |
double mhz(int verbose); |
/* Determine clock rate of processor, having more control over accuracy */ |
double mhz_full(int verbose, int sleeptime); |
/** Special counters that compensate for timer interrupt overhead */ |
void start_comp_counter(); |
double get_comp_counter(); |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/config.h |
---|
0,0 → 1,76 |
#ifndef __CONFIG_H_ |
#define __CONFIG_H_ |
/* |
* config.h - malloc lab configuration file |
* |
* Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. |
* May not be used, modified, or copied without permission. |
*/ |
/* |
* This is the default path where the driver will look for the |
* default tracefiles. You can override it at runtime with the -t flag. |
*/ |
#define TRACEDIR "/home/courses/cs3214/malloclab/traces/" |
/* |
* This is the list of default tracefiles in TRACEDIR that the driver |
* will use for testing. Modify this if you want to add or delete |
* traces from the driver's test suite. For example, if you don't want |
* your students to implement realloc, you can delete the last two |
* traces. |
*/ |
#define DEFAULT_TRACEFILES \ |
"amptjp-bal.rep",\ |
"cccp-bal.rep",\ |
"cp-decl-bal.rep",\ |
"expr-bal.rep",\ |
"coalescing-bal.rep",\ |
"random-bal.rep",\ |
"random2-bal.rep",\ |
"binary-bal.rep",\ |
"binary2-bal.rep",\ |
"realloc-bal.rep",\ |
"realloc2-bal.rep" |
/* |
* This constant gives the estimated performance of the libc malloc |
* package using our traces on some reference system, typically the |
* same kind of system the students use. Its purpose is to cap the |
* contribution of throughput to the performance index. Once the |
* students surpass the AVG_LIBC_THRUPUT, they get no further benefit |
* to their score. This deters students from building extremely fast, |
* but extremely stupid malloc packages. |
* |
* gback@cs.vt.edu: I set this to a value that is achieved by a r/b |
* tree-based implementation on our rlogin cluster as of Fall 2009; |
* it's about twice the speed of the actual libc. |
*/ |
#define AVG_LIBC_THRUPUT 8.2E6 /* 8200 Kops/sec */ |
/* |
* This constant determines the contributions of space utilization |
* (UTIL_WEIGHT) and throughput (1 - UTIL_WEIGHT) to the performance |
* index. |
*/ |
#define UTIL_WEIGHT .60 |
/* |
* Alignment requirement in bytes (either 4 or 8) |
*/ |
#define ALIGNMENT 8 |
/* |
* Maximum heap size in bytes |
*/ |
#define MAX_HEAP (20*(1<<20)) /* 20 MB */ |
/***************************************************************************** |
* Set exactly one of these USE_xxx constants to "1" to select a timing method |
*****************************************************************************/ |
#define USE_FCYC 1 /* cycle counter w/K-best scheme (x86 & Alpha only) */ |
#define USE_ITIMER 0 /* interval timer (any Unix box) */ |
#define USE_GETTOD 0 /* gettimeofday (any Unix box) */ |
#endif /* __CONFIG_H */ |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/fcyc.c |
---|
0,0 → 1,251 |
/* |
* fcyc.c - Estimate the time (in CPU cycles) used by a function f |
* |
* Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. |
* May not be used, modified, or copied without permission. |
* |
* Uses the cycle timer routines in clock.c to estimate the |
* the time in CPU cycles for a function f. |
*/ |
#include <stdlib.h> |
#include <sys/times.h> |
#include <stdio.h> |
#include "fcyc.h" |
#include "clock.h" |
/* Default values */ |
#define K 3 /* Value of K in K-best scheme */ |
#define MAXSAMPLES 20 /* Give up after MAXSAMPLES */ |
#define EPSILON 0.01 /* K samples should be EPSILON of each other*/ |
#define COMPENSATE 0 /* 1-> try to compensate for clock ticks */ |
#define CLEAR_CACHE 0 /* Clear cache before running test function */ |
#define CACHE_BYTES (1<<19) /* Max cache size in bytes */ |
#define CACHE_BLOCK 32 /* Cache block size in bytes */ |
static int kbest = K; |
static int maxsamples = MAXSAMPLES; |
static double epsilon = EPSILON; |
static int compensate = COMPENSATE; |
static int clear_cache = CLEAR_CACHE; |
static int cache_bytes = CACHE_BYTES; |
static int cache_block = CACHE_BLOCK; |
static int *cache_buf = NULL; |
static double *values = NULL; |
static int samplecount = 0; |
/* for debugging only */ |
#define KEEP_VALS 0 |
#define KEEP_SAMPLES 0 |
#if KEEP_SAMPLES |
static double *samples = NULL; |
#endif |
/* |
* init_sampler - Start new sampling process |
*/ |
static void init_sampler() |
{ |
if (values) |
free(values); |
values = calloc(kbest, sizeof(double)); |
#if KEEP_SAMPLES |
if (samples) |
free(samples); |
/* Allocate extra for wraparound analysis */ |
samples = calloc(maxsamples+kbest, sizeof(double)); |
#endif |
samplecount = 0; |
} |
/* |
* add_sample - Add new sample |
*/ |
static void add_sample(double val) |
{ |
int pos = 0; |
if (samplecount < kbest) { |
pos = samplecount; |
values[pos] = val; |
} else if (val < values[kbest-1]) { |
pos = kbest-1; |
values[pos] = val; |
} |
#if KEEP_SAMPLES |
samples[samplecount] = val; |
#endif |
samplecount++; |
/* Insertion sort */ |
while (pos > 0 && values[pos-1] > values[pos]) { |
double temp = values[pos-1]; |
values[pos-1] = values[pos]; |
values[pos] = temp; |
pos--; |
} |
} |
/* |
* has_converged- Have kbest minimum measurements converged within epsilon? |
*/ |
static int has_converged() |
{ |
return |
(samplecount >= kbest) && |
((1 + epsilon)*values[0] >= values[kbest-1]); |
} |
/* |
* clear - Code to clear cache |
*/ |
static volatile int sink = 0; |
static void clear() |
{ |
int x = sink; |
int *cptr, *cend; |
int incr = cache_block/sizeof(int); |
if (!cache_buf) { |
cache_buf = malloc(cache_bytes); |
if (!cache_buf) { |
fprintf(stderr, "Fatal error. Malloc returned null when trying to clear cache\n"); |
exit(1); |
} |
} |
cptr = (int *) cache_buf; |
cend = cptr + cache_bytes/sizeof(int); |
while (cptr < cend) { |
x += *cptr; |
cptr += incr; |
} |
sink = x; |
} |
/* |
* fcyc - Use K-best scheme to estimate the running time of function f |
*/ |
double fcyc(test_funct f, void *argp) |
{ |
double result; |
init_sampler(); |
if (compensate) { |
do { |
double cyc; |
if (clear_cache) |
clear(); |
start_comp_counter(); |
f(argp); |
cyc = get_comp_counter(); |
add_sample(cyc); |
} while (!has_converged() && samplecount < maxsamples); |
} else { |
do { |
double cyc; |
if (clear_cache) |
clear(); |
start_counter(); |
f(argp); |
cyc = get_counter(); |
add_sample(cyc); |
} while (!has_converged() && samplecount < maxsamples); |
} |
#ifdef DEBUG |
{ |
int i; |
printf(" %d smallest values: [", kbest); |
for (i = 0; i < kbest; i++) |
printf("%.0f%s", values[i], i==kbest-1 ? "]\n" : ", "); |
} |
#endif |
result = values[0]; |
#if !KEEP_VALS |
free(values); |
values = NULL; |
#endif |
return result; |
} |
/************************************************************* |
* Set the various parameters used by the measurement routines |
************************************************************/ |
/* |
* set_fcyc_clear_cache - When set, will run code to clear cache |
* before each measurement. |
* Default = 0 |
*/ |
void set_fcyc_clear_cache(int clear) |
{ |
clear_cache = clear; |
} |
/* |
* set_fcyc_cache_size - Set size of cache to use when clearing cache |
* Default = 1<<19 (512KB) |
*/ |
void set_fcyc_cache_size(int bytes) |
{ |
if (bytes != cache_bytes) { |
cache_bytes = bytes; |
if (cache_buf) { |
free(cache_buf); |
cache_buf = NULL; |
} |
} |
} |
/* |
* set_fcyc_cache_block - Set size of cache block |
* Default = 32 |
*/ |
void set_fcyc_cache_block(int bytes) { |
cache_block = bytes; |
} |
/* |
* set_fcyc_compensate- When set, will attempt to compensate for |
* timer interrupt overhead |
* Default = 0 |
*/ |
void set_fcyc_compensate(int compensate_arg) |
{ |
compensate = compensate_arg; |
} |
/* |
* set_fcyc_k - Value of K in K-best measurement scheme |
* Default = 3 |
*/ |
void set_fcyc_k(int k) |
{ |
kbest = k; |
} |
/* |
* set_fcyc_maxsamples - Maximum number of samples attempting to find |
* K-best within some tolerance. |
* When exceeded, just return best sample found. |
* Default = 20 |
*/ |
void set_fcyc_maxsamples(int maxsamples_arg) |
{ |
maxsamples = maxsamples_arg; |
} |
/* |
* set_fcyc_epsilon - Tolerance required for K-best |
* Default = 0.01 |
*/ |
void set_fcyc_epsilon(double epsilon_arg) |
{ |
epsilon = epsilon_arg; |
} |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/fcyc.h |
---|
0,0 → 1,68 |
/* |
* fcyc.h - prototypes for the routines in fcyc.c that estimate the |
* time in CPU cycles used by a test function f |
* |
* Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. |
* May not be used, modified, or copied without permission. |
* |
*/ |
/* The test function takes a generic pointer as input */ |
typedef void (*test_funct)(void *); |
/* Compute number of cycles used by test function f */ |
double fcyc(test_funct f, void* argp); |
/********************************************************* |
* Set the various parameters used by measurement routines |
*********************************************************/ |
/* |
* set_fcyc_clear_cache - When set, will run code to clear cache |
* before each measurement. |
* Default = 0 |
*/ |
void set_fcyc_clear_cache(int clear); |
/* |
* set_fcyc_cache_size - Set size of cache to use when clearing cache |
* Default = 1<<19 (512KB) |
*/ |
void set_fcyc_cache_size(int bytes); |
/* |
* set_fcyc_cache_block - Set size of cache block |
* Default = 32 |
*/ |
void set_fcyc_cache_block(int bytes); |
/* |
* set_fcyc_compensate- When set, will attempt to compensate for |
* timer interrupt overhead |
* Default = 0 |
*/ |
void set_fcyc_compensate(int compensate_arg); |
/* |
* set_fcyc_k - Value of K in K-best measurement scheme |
* Default = 3 |
*/ |
void set_fcyc_k(int k); |
/* |
* set_fcyc_maxsamples - Maximum number of samples attempting to find |
* K-best within some tolerance. |
* When exceeded, just return best sample found. |
* Default = 20 |
*/ |
void set_fcyc_maxsamples(int maxsamples_arg); |
/* |
* set_fcyc_epsilon - Tolerance required for K-best |
* Default = 0.01 |
*/ |
void set_fcyc_epsilon(double epsilon_arg); |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/fsecs.c |
---|
0,0 → 1,57 |
/**************************** |
* High-level timing wrappers |
****************************/ |
#include <stdio.h> |
#include "fsecs.h" |
#include "fcyc.h" |
#include "clock.h" |
#include "ftimer.h" |
#include "config.h" |
static double Mhz; /* estimated CPU clock frequency */ |
extern int verbose; /* -v option in mdriver.c */ |
/* |
* init_fsecs - initialize the timing package |
*/ |
void init_fsecs(void) |
{ |
Mhz = 0; /* keep gcc -Wall happy */ |
#if USE_FCYC |
if (verbose) |
printf("Measuring performance with a cycle counter.\n"); |
/* set key parameters for the fcyc package */ |
set_fcyc_maxsamples(20); |
set_fcyc_clear_cache(1); |
set_fcyc_compensate(1); |
set_fcyc_epsilon(0.01); |
set_fcyc_k(3); |
Mhz = mhz(verbose > 0); |
#elif USE_ITIMER |
if (verbose) |
printf("Measuring performance with the interval timer.\n"); |
#elif USE_GETTOD |
if (verbose) |
printf("Measuring performance with gettimeofday().\n"); |
#endif |
} |
/* |
* fsecs - Return the running time of a function f (in seconds) |
*/ |
double fsecs(fsecs_test_funct f, void *argp) |
{ |
#if USE_FCYC |
double cycles = fcyc(f, argp); |
return cycles/(Mhz*1e6); |
#elif USE_ITIMER |
return ftimer_itimer(f, argp, 10); |
#elif USE_GETTOD |
return ftimer_gettod(f, argp, 10); |
#endif |
} |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/fsecs.h |
---|
0,0 → 1,4 |
typedef void (*fsecs_test_funct)(void *); |
void init_fsecs(void); |
double fsecs(fsecs_test_funct f, void *argp); |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/ftimer.c |
---|
0,0 → 1,106 |
/* |
* ftimer.c - Estimate the time (in seconds) used by a function f |
* |
* Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. |
* May not be used, modified, or copied without permission. |
* |
* Function timers that estimate the running time (in seconds) of a function f. |
* ftimer_itimer: version that uses the interval timer |
* ftimer_gettod: version that uses gettimeofday |
*/ |
#include <stdio.h> |
#include <sys/time.h> |
#include "ftimer.h" |
/* function prototypes */ |
static void init_etime(void); |
static double get_etime(void); |
/* |
* ftimer_itimer - Use the interval timer to estimate the running time |
* of f(argp). Return the average of n runs. |
*/ |
double ftimer_itimer(ftimer_test_funct f, void *argp, int n) |
{ |
double start, tmeas; |
int i; |
init_etime(); |
start = get_etime(); |
for (i = 0; i < n; i++) |
f(argp); |
tmeas = get_etime() - start; |
return tmeas / n; |
} |
/* |
* ftimer_gettod - Use gettimeofday to estimate the running time of |
* f(argp). Return the average of n runs. |
*/ |
double ftimer_gettod(ftimer_test_funct f, void *argp, int n) |
{ |
int i; |
struct timeval stv, etv; |
double diff; |
gettimeofday(&stv, NULL); |
for (i = 0; i < n; i++) |
f(argp); |
gettimeofday(&etv,NULL); |
diff = 1E3*(etv.tv_sec - stv.tv_sec) + 1E-3*(etv.tv_usec-stv.tv_usec); |
diff /= n; |
return (1E-3*diff); |
} |
/* |
* Routines for manipulating the Unix interval timer |
*/ |
/* The initial value of the interval timer */ |
#define MAX_ETIME 86400 |
/* static variables that hold the initial value of the interval timer */ |
static struct itimerval first_u; /* user time */ |
static struct itimerval first_r; /* real time */ |
static struct itimerval first_p; /* prof time*/ |
/* init the timer */ |
static void init_etime(void) |
{ |
first_u.it_interval.tv_sec = 0; |
first_u.it_interval.tv_usec = 0; |
first_u.it_value.tv_sec = MAX_ETIME; |
first_u.it_value.tv_usec = 0; |
setitimer(ITIMER_VIRTUAL, &first_u, NULL); |
first_r.it_interval.tv_sec = 0; |
first_r.it_interval.tv_usec = 0; |
first_r.it_value.tv_sec = MAX_ETIME; |
first_r.it_value.tv_usec = 0; |
setitimer(ITIMER_REAL, &first_r, NULL); |
first_p.it_interval.tv_sec = 0; |
first_p.it_interval.tv_usec = 0; |
first_p.it_value.tv_sec = MAX_ETIME; |
first_p.it_value.tv_usec = 0; |
setitimer(ITIMER_PROF, &first_p, NULL); |
} |
/* return elapsed real seconds since call to init_etime */ |
static double get_etime(void) { |
struct itimerval v_curr; |
struct itimerval r_curr; |
struct itimerval p_curr; |
getitimer(ITIMER_VIRTUAL, &v_curr); |
getitimer(ITIMER_REAL,&r_curr); |
getitimer(ITIMER_PROF,&p_curr); |
return (double) ((first_p.it_value.tv_sec - r_curr.it_value.tv_sec) + |
(first_p.it_value.tv_usec - r_curr.it_value.tv_usec)*1e-6); |
} |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/ftimer.h |
---|
0,0 → 1,14 |
/* |
* Function timers |
*/ |
typedef void (*ftimer_test_funct)(void *); |
/* Estimate the running time of f(argp) using the Unix interval timer. |
Return the average of n runs */ |
double ftimer_itimer(ftimer_test_funct f, void *argp, int n); |
/* Estimate the running time of f(argp) using gettimeofday |
Return the average of n runs */ |
double ftimer_gettod(ftimer_test_funct f, void *argp, int n); |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/list.c |
---|
0,0 → 1,532 |
#include "list.h" |
#include <assert.h> |
/* Our doubly linked lists have two header elements: the "head" |
just before the first element and the "tail" just after the |
last element. The `prev' link of the front header is null, as |
is the `next' link of the back header. Their other two links |
point toward each other via the interior elements of the list. |
An empty list looks like this: |
+------+ +------+ |
<---| head |<--->| tail |---> |
+------+ +------+ |
A list with two elements in it looks like this: |
+------+ +-------+ +-------+ +------+ |
<---| head |<--->| 1 |<--->| 2 |<--->| tail |<---> |
+------+ +-------+ +-------+ +------+ |
The symmetry of this arrangement eliminates lots of special |
cases in list processing. For example, take a look at |
list_remove(): it takes only two pointer assignments and no |
conditionals. That's a lot simpler than the code would be |
without header elements. |
(Because only one of the pointers in each header element is used, |
we could in fact combine them into a single header element |
without sacrificing this simplicity. But using two separate |
elements allows us to do a little bit of checking on some |
operations, which can be valuable.) */ |
static bool is_sorted (struct list_elem *a, struct list_elem *b, |
list_less_func *less, void *aux); |
/* Returns true if ELEM is a head, false otherwise. */ |
static inline bool |
is_head (struct list_elem *elem) |
{ |
return elem != NULL && elem->prev == NULL && elem->next != NULL; |
} |
/* Returns true if ELEM is an interior element, |
false otherwise. */ |
static inline bool |
is_interior (struct list_elem *elem) |
{ |
return elem != NULL && elem->prev != NULL && elem->next != NULL; |
} |
/* Returns true if ELEM is a tail, false otherwise. */ |
static inline bool |
is_tail (struct list_elem *elem) |
{ |
return elem != NULL && elem->prev != NULL && elem->next == NULL; |
} |
/* Initializes LIST as an empty list. */ |
void |
list_init (struct list *list) |
{ |
assert (list != NULL); |
list->head.prev = NULL; |
list->head.next = &list->tail; |
list->tail.prev = &list->head; |
list->tail.next = NULL; |
} |
/* Returns the beginning of LIST. */ |
struct list_elem * |
list_begin (struct list *list) |
{ |
assert (list != NULL); |
return list->head.next; |
} |
/* Returns the element after ELEM in its list. If ELEM is the |
last element in its list, returns the list tail. Results are |
undefined if ELEM is itself a list tail. */ |
struct list_elem * |
list_next (struct list_elem *elem) |
{ |
assert (is_head (elem) || is_interior (elem)); |
return elem->next; |
} |
/* Returns LIST's tail. |
list_end() is often used in iterating through a list from |
front to back. See the big comment at the top of list.h for |
an example. */ |
struct list_elem * |
list_end (struct list *list) |
{ |
assert (list != NULL); |
return &list->tail; |
} |
/* Returns the LIST's reverse beginning, for iterating through |
LIST in reverse order, from back to front. */ |
struct list_elem * |
list_rbegin (struct list *list) |
{ |
assert (list != NULL); |
return list->tail.prev; |
} |
/* Returns the element before ELEM in its list. If ELEM is the |
first element in its list, returns the list head. Results are |
undefined if ELEM is itself a list head. */ |
struct list_elem * |
list_prev (struct list_elem *elem) |
{ |
assert (is_interior (elem) || is_tail (elem)); |
return elem->prev; |
} |
/* Returns LIST's head. |
list_rend() is often used in iterating through a list in |
reverse order, from back to front. Here's typical usage, |
following the example from the top of list.h: |
for (e = list_rbegin (&foo_list); e != list_rend (&foo_list); |
e = list_prev (e)) |
{ |
struct foo *f = list_entry (e, struct foo, elem); |
...do something with f... |
} |
*/ |
struct list_elem * |
list_rend (struct list *list) |
{ |
assert (list != NULL); |
return &list->head; |
} |
/* Return's LIST's head. |
list_head() can be used for an alternate style of iterating |
through a list, e.g.: |
e = list_head (&list); |
while ((e = list_next (e)) != list_end (&list)) |
{ |
... |
} |
*/ |
struct list_elem * |
list_head (struct list *list) |
{ |
assert (list != NULL); |
return &list->head; |
} |
/* Return's LIST's tail. */ |
struct list_elem * |
list_tail (struct list *list) |
{ |
assert (list != NULL); |
return &list->tail; |
} |
/* Inserts ELEM just before BEFORE, which may be either an |
interior element or a tail. The latter case is equivalent to |
list_push_back(). */ |
void |
list_insert (struct list_elem *before, struct list_elem *elem) |
{ |
assert (is_interior (before) || is_tail (before)); |
assert (elem != NULL); |
elem->prev = before->prev; |
elem->next = before; |
before->prev->next = elem; |
before->prev = elem; |
} |
/* Removes elements FIRST though LAST (exclusive) from their |
current list, then inserts them just before BEFORE, which may |
be either an interior element or a tail. */ |
void |
list_splice (struct list_elem *before, |
struct list_elem *first, struct list_elem *last) |
{ |
assert (is_interior (before) || is_tail (before)); |
if (first == last) |
return; |
last = list_prev (last); |
assert (is_interior (first)); |
assert (is_interior (last)); |
/* Cleanly remove FIRST...LAST from its current list. */ |
first->prev->next = last->next; |
last->next->prev = first->prev; |
/* Splice FIRST...LAST into new list. */ |
first->prev = before->prev; |
last->next = before; |
before->prev->next = first; |
before->prev = last; |
} |
/* Inserts ELEM at the beginning of LIST, so that it becomes the |
front in LIST. */ |
void |
list_push_front (struct list *list, struct list_elem *elem) |
{ |
list_insert (list_begin (list), elem); |
} |
/* Inserts ELEM at the end of LIST, so that it becomes the |
back in LIST. */ |
void |
list_push_back (struct list *list, struct list_elem *elem) |
{ |
list_insert (list_end (list), elem); |
} |
/* Removes ELEM from its list and returns the element that |
followed it. Undefined behavior if ELEM is not in a list. |
It's not safe to treat ELEM as an element in a list after |
removing it. In particular, using list_next() or list_prev() |
on ELEM after removal yields undefined behavior. This means |
that a naive loop to remove the elements in a list will fail: |
** DON'T DO THIS ** |
for (e = list_begin (&list); e != list_end (&list); e = list_next (e)) |
{ |
...do something with e... |
list_remove (e); |
} |
** DON'T DO THIS ** |
Here is one correct way to iterate and remove elements from a |
list: |
for (e = list_begin (&list); e != list_end (&list); e = list_remove (e)) |
{ |
...do something with e... |
} |
If you need to free() elements of the list then you need to be |
more conservative. Here's an alternate strategy that works |
even in that case: |
while (!list_empty (&list)) |
{ |
struct list_elem *e = list_pop_front (&list); |
...do something with e... |
} |
*/ |
struct list_elem * |
list_remove (struct list_elem *elem) |
{ |
assert (is_interior (elem)); |
elem->prev->next = elem->next; |
elem->next->prev = elem->prev; |
return elem->next; |
} |
/* Removes the front element from LIST and returns it. |
Undefined behavior if LIST is empty before removal. */ |
struct list_elem * |
list_pop_front (struct list *list) |
{ |
struct list_elem *front = list_front (list); |
list_remove (front); |
return front; |
} |
/* Removes the back element from LIST and returns it. |
Undefined behavior if LIST is empty before removal. */ |
struct list_elem * |
list_pop_back (struct list *list) |
{ |
struct list_elem *back = list_back (list); |
list_remove (back); |
return back; |
} |
/* Returns the front element in LIST. |
Undefined behavior if LIST is empty. */ |
struct list_elem * |
list_front (struct list *list) |
{ |
assert (!list_empty (list)); |
return list->head.next; |
} |
/* Returns the back element in LIST. |
Undefined behavior if LIST is empty. */ |
struct list_elem * |
list_back (struct list *list) |
{ |
assert (!list_empty (list)); |
return list->tail.prev; |
} |
/* Returns the number of elements in LIST. |
Runs in O(n) in the number of elements. */ |
size_t |
list_size (struct list *list) |
{ |
struct list_elem *e; |
size_t cnt = 0; |
for (e = list_begin (list); e != list_end (list); e = list_next (e)) |
cnt++; |
return cnt; |
} |
/* Returns true if LIST is empty, false otherwise. */ |
bool |
list_empty (struct list *list) |
{ |
return list_begin (list) == list_end (list); |
} |
/* Swaps the `struct list_elem *'s that A and B point to. */ |
static void |
swap (struct list_elem **a, struct list_elem **b) |
{ |
struct list_elem *t = *a; |
*a = *b; |
*b = t; |
} |
/* Reverses the order of LIST. */ |
void |
list_reverse (struct list *list) |
{ |
if (!list_empty (list)) |
{ |
struct list_elem *e; |
for (e = list_begin (list); e != list_end (list); e = e->prev) |
swap (&e->prev, &e->next); |
swap (&list->head.next, &list->tail.prev); |
swap (&list->head.next->prev, &list->tail.prev->next); |
} |
} |
/* Returns true only if the list elements A through B (exclusive) |
are in order according to LESS given auxiliary data AUX. */ |
static bool |
is_sorted (struct list_elem *a, struct list_elem *b, |
list_less_func *less, void *aux) |
{ |
if (a != b) |
while ((a = list_next (a)) != b) |
if (less (a, list_prev (a), aux)) |
return false; |
return true; |
} |
/* Finds a run, starting at A and ending not after B, of list |
elements that are in nondecreasing order according to LESS |
given auxiliary data AUX. Returns the (exclusive) end of the |
run. |
A through B (exclusive) must form a non-empty range. */ |
static struct list_elem * |
find_end_of_run (struct list_elem *a, struct list_elem *b, |
list_less_func *less, void *aux) |
{ |
assert (a != NULL); |
assert (b != NULL); |
assert (less != NULL); |
assert (a != b); |
do |
{ |
a = list_next (a); |
} |
while (a != b && !less (a, list_prev (a), aux)); |
return a; |
} |
/* Merges A0 through A1B0 (exclusive) with A1B0 through B1 |
(exclusive) to form a combined range also ending at B1 |
(exclusive). Both input ranges must be nonempty and sorted in |
nondecreasing order according to LESS given auxiliary data |
AUX. The output range will be sorted the same way. */ |
static void |
inplace_merge (struct list_elem *a0, struct list_elem *a1b0, |
struct list_elem *b1, |
list_less_func *less, void *aux) |
{ |
assert (a0 != NULL); |
assert (a1b0 != NULL); |
assert (b1 != NULL); |
assert (less != NULL); |
assert (is_sorted (a0, a1b0, less, aux)); |
assert (is_sorted (a1b0, b1, less, aux)); |
while (a0 != a1b0 && a1b0 != b1) |
if (!less (a1b0, a0, aux)) |
a0 = list_next (a0); |
else |
{ |
a1b0 = list_next (a1b0); |
list_splice (a0, list_prev (a1b0), a1b0); |
} |
} |
/* Sorts LIST according to LESS given auxiliary data AUX, using a |
natural iterative merge sort that runs in O(n lg n) time and |
O(1) space in the number of elements in LIST. */ |
void |
list_sort (struct list *list, list_less_func *less, void *aux) |
{ |
size_t output_run_cnt; /* Number of runs output in current pass. */ |
assert (list != NULL); |
assert (less != NULL); |
/* Pass over the list repeatedly, merging adjacent runs of |
nondecreasing elements, until only one run is left. */ |
do |
{ |
struct list_elem *a0; /* Start of first run. */ |
struct list_elem *a1b0; /* End of first run, start of second. */ |
struct list_elem *b1; /* End of second run. */ |
output_run_cnt = 0; |
for (a0 = list_begin (list); a0 != list_end (list); a0 = b1) |
{ |
/* Each iteration produces one output run. */ |
output_run_cnt++; |
/* Locate two adjacent runs of nondecreasing elements |
A0...A1B0 and A1B0...B1. */ |
a1b0 = find_end_of_run (a0, list_end (list), less, aux); |
if (a1b0 == list_end (list)) |
break; |
b1 = find_end_of_run (a1b0, list_end (list), less, aux); |
/* Merge the runs. */ |
inplace_merge (a0, a1b0, b1, less, aux); |
} |
} |
while (output_run_cnt > 1); |
assert (is_sorted (list_begin (list), list_end (list), less, aux)); |
} |
/* Inserts ELEM in the proper position in LIST, which must be |
sorted according to LESS given auxiliary data AUX. |
Runs in O(n) average case in the number of elements in LIST. */ |
void |
list_insert_ordered (struct list *list, struct list_elem *elem, |
list_less_func *less, void *aux) |
{ |
struct list_elem *e; |
assert (list != NULL); |
assert (elem != NULL); |
assert (less != NULL); |
for (e = list_begin (list); e != list_end (list); e = list_next (e)) |
if (less (elem, e, aux)) |
break; |
return list_insert (e, elem); |
} |
/* Iterates through LIST and removes all but the first in each |
set of adjacent elements that are equal according to LESS |
given auxiliary data AUX. If DUPLICATES is non-null, then the |
elements from LIST are appended to DUPLICATES. */ |
void |
list_unique (struct list *list, struct list *duplicates, |
list_less_func *less, void *aux) |
{ |
struct list_elem *elem, *next; |
assert (list != NULL); |
assert (less != NULL); |
if (list_empty (list)) |
return; |
elem = list_begin (list); |
while ((next = list_next (elem)) != list_end (list)) |
if (!less (elem, next, aux) && !less (next, elem, aux)) |
{ |
list_remove (next); |
if (duplicates != NULL) |
list_push_back (duplicates, next); |
} |
else |
elem = next; |
} |
/* Returns the element in LIST with the largest value according |
to LESS given auxiliary data AUX. If there is more than one |
maximum, returns the one that appears earlier in the list. If |
the list is empty, returns its tail. */ |
struct list_elem * |
list_max (struct list *list, list_less_func *less, void *aux) |
{ |
struct list_elem *max = list_begin (list); |
if (max != list_end (list)) |
{ |
struct list_elem *e; |
for (e = list_next (max); e != list_end (list); e = list_next (e)) |
if (less (max, e, aux)) |
max = e; |
} |
return max; |
} |
/* Returns the element in LIST with the smallest value according |
to LESS given auxiliary data AUX. If there is more than one |
minimum, returns the one that appears earlier in the list. If |
the list is empty, returns its tail. */ |
struct list_elem * |
list_min (struct list *list, list_less_func *less, void *aux) |
{ |
struct list_elem *min = list_begin (list); |
if (min != list_end (list)) |
{ |
struct list_elem *e; |
for (e = list_next (min); e != list_end (list); e = list_next (e)) |
if (less (e, min, aux)) |
min = e; |
} |
return min; |
} |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/list.h |
---|
0,0 → 1,170 |
#ifndef __LIST_H |
#define __LIST_H |
/* This code is taken from the Pintos education OS. |
* For copyright information, see www.pintos-os.org */ |
/* Doubly linked list. |
This implementation of a doubly linked list does not require |
use of dynamically allocated memory. Instead, each structure |
that is a potential list element must embed a struct list_elem |
member. All of the list functions operate on these `struct |
list_elem's. The list_entry macro allows conversion from a |
struct list_elem back to a structure object that contains it. |
For example, suppose there is a needed for a list of `struct |
foo'. `struct foo' should contain a `struct list_elem' |
member, like so: |
struct foo |
{ |
struct list_elem elem; |
int bar; |
...other members... |
}; |
Then a list of `struct foo' can be be declared and initialized |
like so: |
struct list foo_list; |
list_init (&foo_list); |
Iteration is a typical situation where it is necessary to |
convert from a struct list_elem back to its enclosing |
structure. Here's an example using foo_list: |
struct list_elem *e; |
for (e = list_begin (&foo_list); e != list_end (&foo_list); |
e = list_next (e)) |
{ |
struct foo *f = list_entry (e, struct foo, elem); |
...do something with f... |
} |
You can find real examples of list usage throughout the |
source; for example, malloc.c, palloc.c, and thread.c in the |
threads directory all use lists. |
The interface for this list is inspired by the list<> template |
in the C++ STL. If you're familiar with list<>, you should |
find this easy to use. However, it should be emphasized that |
these lists do *no* type checking and can't do much other |
correctness checking. If you screw up, it will bite you. |
Glossary of list terms: |
- "front": The first element in a list. Undefined in an |
empty list. Returned by list_front(). |
- "back": The last element in a list. Undefined in an empty |
list. Returned by list_back(). |
- "tail": The element figuratively just after the last |
element of a list. Well defined even in an empty list. |
Returned by list_end(). Used as the end sentinel for an |
iteration from front to back. |
- "beginning": In a non-empty list, the front. In an empty |
list, the tail. Returned by list_begin(). Used as the |
starting point for an iteration from front to back. |
- "head": The element figuratively just before the first |
element of a list. Well defined even in an empty list. |
Returned by list_rend(). Used as the end sentinel for an |
iteration from back to front. |
- "reverse beginning": In a non-empty list, the back. In an |
empty list, the head. Returned by list_rbegin(). Used as |
the starting point for an iteration from back to front. |
- "interior element": An element that is not the head or |
tail, that is, a real list element. An empty list does |
not have any interior elements. |
*/ |
#include <stdbool.h> |
#include <stddef.h> |
#include <stdint.h> |
/* List element. */ |
struct list_elem |
{ |
struct list_elem *prev; /* Previous list element. */ |
struct list_elem *next; /* Next list element. */ |
}; |
/* List. */ |
struct list |
{ |
struct list_elem head; /* List head. */ |
struct list_elem tail; /* List tail. */ |
}; |
/* Converts pointer to list element LIST_ELEM into a pointer to |
the structure that LIST_ELEM is embedded inside. Supply the |
name of the outer structure STRUCT and the member name MEMBER |
of the list element. See the big comment at the top of the |
file for an example. */ |
#define list_entry(LIST_ELEM, STRUCT, MEMBER) \ |
((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \ |
- offsetof (STRUCT, MEMBER.next))) |
void list_init (struct list *); |
/* List traversal. */ |
struct list_elem *list_begin (struct list *); |
struct list_elem *list_next (struct list_elem *); |
struct list_elem *list_end (struct list *); |
struct list_elem *list_rbegin (struct list *); |
struct list_elem *list_prev (struct list_elem *); |
struct list_elem *list_rend (struct list *); |
struct list_elem *list_head (struct list *); |
struct list_elem *list_tail (struct list *); |
/* List insertion. */ |
void list_insert (struct list_elem *, struct list_elem *); |
void list_splice (struct list_elem *before, |
struct list_elem *first, struct list_elem *last); |
void list_push_front (struct list *, struct list_elem *); |
void list_push_back (struct list *, struct list_elem *); |
/* List removal. */ |
struct list_elem *list_remove (struct list_elem *); |
struct list_elem *list_pop_front (struct list *); |
struct list_elem *list_pop_back (struct list *); |
/* List elements. */ |
struct list_elem *list_front (struct list *); |
struct list_elem *list_back (struct list *); |
/* List properties. */ |
size_t list_size (struct list *); |
bool list_empty (struct list *); |
/* Miscellaneous. */ |
void list_reverse (struct list *); |
/* Compares the value of two list elements A and B, given |
auxiliary data AUX. Returns true if A is less than B, or |
false if A is greater than or equal to B. */ |
typedef bool list_less_func (const struct list_elem *a, |
const struct list_elem *b, |
void *aux); |
/* Operations on lists with ordered elements. */ |
void list_sort (struct list *, |
list_less_func *, void *aux); |
void list_insert_ordered (struct list *, struct list_elem *, |
list_less_func *, void *aux); |
void list_unique (struct list *, struct list *duplicates, |
list_less_func *, void *aux); |
/* Max and min. */ |
struct list_elem *list_max (struct list *, list_less_func *, void *aux); |
struct list_elem *list_min (struct list *, list_less_func *, void *aux); |
#endif /* list.h */ |
/Classwork/CS3214 - Computer Systems/Project 3 - Malloc Lab/malloclab-cs3214.pdf |
---|
0,0 → 1,1825 |
%PDF-1.4 |
5 0 obj |
<< /S /GoTo /D (section.1) >> |
endobj |
8 0 obj |
(Introduction) |
endobj |
9 0 obj |
<< /S /GoTo /D (section.2) >> |
endobj |
12 0 obj |
(Logistics) |
endobj |
13 0 obj |
<< /S /GoTo /D (section.3) >> |
endobj |
16 0 obj |
(Hand Out Instructions) |
endobj |
17 0 obj |
<< /S /GoTo /D (section.4) >> |
endobj |
20 0 obj |
(How to Work on the Lab) |
endobj |
21 0 obj |
<< /S /GoTo /D (section.5) >> |
endobj |
24 0 obj |
(Heap Consistency Checker) |
endobj |
25 0 obj |
<< /S /GoTo /D (section.6) >> |
endobj |
28 0 obj |
(Support Routines) |
endobj |
29 0 obj |
<< /S /GoTo /D (section.7) >> |
endobj |
32 0 obj |
(The Trace-driven Driver Program) |
endobj |
33 0 obj |
<< /S /GoTo /D (section.8) >> |
endobj |
36 0 obj |
(Programming Rules) |
endobj |
37 0 obj |
<< /S /GoTo /D (section.9) >> |
endobj |
40 0 obj |
(Evaluation) |
endobj |
41 0 obj |
<< /S /GoTo /D (section.10) >> |
endobj |
44 0 obj |
(Handin Instructions) |
endobj |
45 0 obj |
<< /S /GoTo /D (section.11) >> |
endobj |
48 0 obj |
(Hints) |
endobj |
49 0 obj |
<< /S /GoTo /D [50 0 R /Fit ] >> |
endobj |
52 0 obj << |
/Length 1595 |
/Filter /FlateDecode |
>> |
stream |
xÚWKÛ6¾çW¹DV\Ô3( |
+ßÓa§¼KGKÓÚrðµ¡78ÿE¢¬AßÍÕAe¶T8'qÄâÌFk /÷f[Û¡.íªeö®Hä»#¦ÿJ¬º#¢éÖhOSÌL\æGC½6à$ð¯:¯¾lT_»ë*,jK²þ¾@j{^¼>FgÅ0:ï/TÌTßÕ7{cíýiº)4~7Æö)NÞÉ?Ïáù8ú ßuvè §âúÏèzÌÊ<ó#¥¶ |
+yÏ´¡{Nù\x&yV<IuU§¦CUí½ûý¦ (ì§@Ã(K1Écá!|Èb2àøk#I5² |
+YÎHÐ!ßjî´æoQæ®mºLß+±uûj°^îTpr³xF+ë¦3C½ñx0ú²¶Æ£)g@°8/o§D<ª'êªôö¹5I\+¨ê KÎÏp9Æßã)ºrÕ ÒTn-!«yiÓãC\fóä2³y±,_²_0x|&ÒbsK¼Ïg*¤¥p.\$óàcç-´jju¡;γ+è~ÐíÎͲM?LÕ?òãëù ÎÍÒJÇÉÓBìüúå¬HÓغ¬HrNO2¾=õâs#ÔS8=öТéH3¯ËØí-á |¼s7{ûòö¶<XVZv®Æ[oxû *Ûö.#ÝQÕg¯º2/[mkPìù]¥K/%F8, ûH:m¯Í°àDPÌe,1æVKlW_È uµÓåWbe·Ûpªz==-çbi80aoúäÇ=v¶WJô7|Yh{z¡ã¸«ËGs2ÎÍ ~TÚ¶~hæ$[HÍs8^LXðmJis²K۵ݽ¸6`*R0ÅVâ@EÊà"Apz}*°JÓ |
+àt`(º±¥BÚ50bcиöOFñùÓ5§K¢ýû!§÷(z? 1´C¤ÇT ã«p)L$ |
+pãüKý¶ëX§>ºmÙb_¹Ìs>5ÀpQ¤Ýh N¬îµÓdÉÁ¸GzÁDð/KfCÔW_yxÁzñ«ÅðMßP[:JÑÉE·ªÜAî-¿_þ{pøÏfÅܾ·Ur¿ìh£r&e/u î§çîÂQá RI¬ïd©»vÒζP¿ËF}PÍHÕì6iÛëú(@ñÒ¯¸ HYÓØâ4~³Ôé ³à0«`yþÇ |
+kÏ~ºö/L3endstream |
+endobj |
+50 0 obj << |
+/Type /Page |
+/Contents 52 0 R |
+/Resources 51 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 67 0 R |
+>> endobj |
+53 0 obj << |
+/D [50 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+54 0 obj << |
+/D [50 0 R /XYZ 72 662.2167 null] |
+>> endobj |
+6 0 obj << |
+/D [50 0 R /XYZ 72 499.7486 null] |
+>> endobj |
+10 0 obj << |
+/D [50 0 R /XYZ 72 399.1599 null] |
+>> endobj |
+14 0 obj << |
+/D [50 0 R /XYZ 72 315.1967 null] |
+>> endobj |
+51 0 obj << |
+/Font << /F37 57 0 R /F38 60 0 R /F40 63 0 R /F15 66 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+70 0 obj << |
+/Length 2804 |
+/Filter /FlateDecode |
+>> |
+stream |
+4ugÇÉ®^Ô'>|{óÅ×:]ÈXdq&÷T-+ ²©[Ü?D㦺]j¥£¢Ûnó¶¼ýñþ/¾6ñÅX¤ÒèEìwlóÇV]RVHãÄòª±#²ëªú|äC¦Óʾ~ªúprÉ£x¾oô·.Ú·Ø6Q=ÒØ¡7WT>³rVXk³#¾Ø#8ÿ»Aê56ÜSàÅÒ$FXÛÅRjÎüÛø~SÍlD¢dÃüIÒ |
+DÝßxEUî=ìy>ò¢èötU<£ëOÌܦÚæ5Cj^"C5V w®N Uâ] êõ&Ù!gpxWê]ûÏáÀáºÅ |
+.þ©P´býÆ&ª¨ OÛT¤GÐ%ùk5ùêølÁ?NnÈ×ÂXÍ»¶9Sfsæ¶çLKÑXËW,[%<7=Ù¿H;æ`KÓM{F"HÈìò®Ù{÷:¤è¯Uµé:GNËehsIqH sy{¤¡°ö0ï2~'ö |
+»[0æ7OáxÎË#ßù¼¸9È |
+î#õùÛ÷ùjîu::©OÑù5YT(ë:aùº¢ÎyXæ½´ÉHü3@Çë£ úö<æS7ô°oÉÀòc#ôGM¡rTMͦÌÀÂlfAÀaUs |
+£Ê |
+¥ÞeöxlÜÿ2b<§à_BÁSg/íЩmªÛzD¼{êê¡êwsn'ý¸Fļj)ùüI00³q ¾õä1~üi¤Û§£:Ã09q)1ùÐWU`ÒoyèDÛ_ÅÈnìÏä²ÿïöê}5]ñÄ\lüU4¹»XfEôÌoÊ4áÎõ¥M @Pýz4ê¦Lnr6þß'#kH.[ä1=áÏ@´¥pF^QPÃ#¹ghAiUÑõd¯&=¢÷³ |
+b^>ß./=ÈL³ 7)2qÂöF»ð§*Ñ¿(VäéfóðOÃ÷#/ÌÉbÅs>eÃöjà3'Ü¡.j\¶ë ÕÁÇÌÙZ[v³Ðö «s×Sát³¯l@ñàÇë&9D9ç8 çP¯[ Öý稻!ÞÀ¾¾äÓÍ¥Þ*-RéÌ¥Þ~e:Èp3(îuú)Aj±tj¡I|zþ9Q\ô èk2 |
+{*%Þ]úý¾Ou³©FÄhõ¦Êw¤<ì*¥¯¢?Ãs_IM|dUÆu¦qßc |
+.L¥°*¥¢uÞ¾¤ÁuágËsi@!XìÓ,<òÜÛe)~ÚLèc |
+=Ãý¿¿]¦.¥¼](4%WuIX/ z÷þzÕg}©ÚfÂÉ$8+Øò+ l)øïêÖDÉ\(_@6¢~å ¬ùv |
+Þ§'Wþ÷ùÓ·ûêÛûg¼coíhÆ_}í@CUßÑËàÏ=²Á/5ÁêõAI¶Oõ82bcµ²dk 1¬ÏlXyÐÚ§Fãendstream |
+endobj |
+69 0 obj << |
+/Type /Page |
+/Contents 70 0 R |
+/Resources 68 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 67 0 R |
+>> endobj |
+71 0 obj << |
+/D [69 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+18 0 obj << |
+/D [69 0 R /XYZ 72 467.1424 null] |
+>> endobj |
+68 0 obj << |
+/Font << /F37 57 0 R /F40 63 0 R /F38 60 0 R /F45 74 0 R /F34 77 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+80 0 obj << |
+/Length 2562 |
+/Filter /FlateDecode |
+>> |
+stream |
+ æ5Ûÿ¦ÞÜèéµp,±ÔXpþµiýhWÔndåCÖunCêÒº/TѨ#ÌGDlp1OAæÉÅNðldµ#¿ÈÚ}Y´^(4%S6Jí÷ LE×G\°u)J®Í%a*ßnëãf2fëRÃX'[ |
+(YÍeõÖiyÖð78~ðP iã(xþxÌ³Ü ájäs"J¯(# |
+áXºÇÆ÷8æhKr5£PnX4G¤ra"Ã?5ÆA³×jÿe.o ¬ÃqËÖOeS»©æÑCöî_d]õ%dÕºòÏxÆ3S[|8õ ÷ײßù0hÓ:Ad¾BzqySw}[]ð |
+HÐv¿¶½²·1rn?6V~¦'æPükýngIm íªS-¯%Ò$Dïf¿õ'[cwP=$#×_â3ÉL¦ßO]©Ey8]ûy½(¼-Èvë;¿ÇÖHO¨W·Ç²D~kú2/¼ |
+"Ð>ò |
+vXE |
+x |
+%Láß)F/°çoÁÊÜÆÃ]¼p`«p¼w {g±p<:µIPûÌ #é¡G#±ð±T%ªásY{(O·±õ,?B¿^°Bf$ÁMÀf¼Te |
+Ø ¤{îåÅÝ°µ^ ãîu¢{{)6Saæ¤ñµÀÙ0?ê>¤øã¥Ê4¨GÒÈøR¡¹ÁjB9! [ã**CÐ8cYïÜó¨· 6¼ëPí6Ï÷ÆeÊX1b¥Bż/ræ|c@sÜ]´äY¹¡²,IôýH3\ú{ÝÙHÉ NÎ>ØÍ )¸Méút&^ÍHÆÔÒË"6BÆ |
+^ "HÌGÁkC§2Ò¢8î{7éâ3ÓZpê¡È³cç±õÃ!eýÒ`Øzñ+PQEm=Þá2Ø éÔåá¸÷êÒõ¿oSÈ2ǯe0$9¥9¦ý³F±?<d¯m¬*`Á8roÞ<3±Ë³º[Øì¸i¨`íînNÌÄG»cOôcðªÚ1ö¹©<z_-,bæë@ÒeÒ}ã0Ìû=NmßÇoXâö7\Cxrñxsé²ëçp8 |
+>$D½¼A÷ÏHîk_LQeísá%£ì_ã/N~|æÁéÍÌß6ýÌêÓ ²¾|:ßÆ(·<voL4¸k^½DÀp§¼É@9ä«sÑ@VÏ]Þ!^>¬«Sûã2üûHÐýc·Í}ûêµíyeýßXLfúµ°³Ï¡!¹¾4ßÄI\ä$Ð$ åSA.R|Z;áp+ø_¦ø>endstream |
+endobj |
+79 0 obj << |
+/Type /Page |
+/Contents 80 0 R |
+/Resources 78 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 67 0 R |
+>> endobj |
+81 0 obj << |
+/D [79 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+22 0 obj << |
+/D [79 0 R /XYZ 72 298.7129 null] |
+>> endobj |
+78 0 obj << |
+/Font << /F34 77 0 R /F40 63 0 R /F37 57 0 R /F38 60 0 R /F45 74 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+84 0 obj << |
+/Length 2790 |
+/Filter /FlateDecode |
+>> |
+stream |
+è;Úå¡ÙOz®]·k¯Óå~íê í5O®ß·u)§GÝÔ_ÿØãǼÚ;úVÞ®õ:HWW´ädÐQ |
+Êfp·,åêåî~า°3"1ãÔ`*j.ËÞ¸ àQUóDµ7<¶4¶ßþa³Û{¨åûþP9¡P¥¢Ð=wMI<3q¦L/ïQÊG |
+>'ªaFuZ0i¤ =ÿº¨ið±@¯e0½ô}?$ÜÎNÄSF¿hÀ5lK=áãýªÐz1¡À§[kðP ©uCÿnÉãjÊrfKr#ÇöÐeìîZ|YísÉqÆ(YZmeo?ï`¡!1+òöF8@ëî0¯Þ©Ìç¢r)xv{Ý éêDùMØ3Üà¨å71ùCÓc×tC?ºK¦)! ñjgÏCîÁ'Ø$Ôø è®v-n=¤ùÀ@Cyðm"èD´þÄTÔ}n9íW»§ê&¡ýëÞ"3û6ø·½-¬Û{Ûp[8k¦7/%JHèÕKÏMøþK]~?=¢Ë¢Ê|³É77÷¹p»>N÷óèÌ(ã«,×rPAºP0JrË_ä«)ǹN)&RC¿Ë;./pé]ÌqMNþئDI«cO<F,Rt×Å6ó|¤|g:üUíçT/²)©Ï©üpez!![ Þu9®¦,ç/òãÔçaBÁz´QÿüåÝ»p*¨Ðà%?vLLý4hó~ãÚ cØ2¶b¼ÓÑm§¨%MÂ,ºÁ×ZSç¢ÖÐe¾ìHÈ@ çÁW2r¼(d°ÓرUs©ØtµøÚÀÏcå%¡ÊK2 X |
+Ť>0$Zc ((Ã:î*|pÚàj"àÌwL![ó°<¸Øï2à^MÈp w$dù= Wåßm]ùÅ}e¬xóqÍ}'®&çL¡2±Jã3Y4Å«É^Óø5ÍÚ¼: È3ø,^ì©:ébÙÒ[ë´ :¢ã8>Ó0¤Ùï g#285h£_2Sç03ôûFм¨j.as$êëÍ¿7jxU+þÈ;_éZð#t |
+ |
+þ÷ÂpwjMX4Fs#5 ÇÚ rî±/ÁAÀÙÛAæ(êì[CÖã=d·ù¦ñQîXÈãÀ(ãÇ Äg k©'1{ö¯KX¡´±¦ ¡ø¾|`˵B*Óf |
+KdW}Ûÿ Ô{nªendstream |
+endobj |
+83 0 obj << |
+/Type /Page |
+/Contents 84 0 R |
+/Resources 82 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 67 0 R |
+>> endobj |
+85 0 obj << |
+/D [83 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+26 0 obj << |
+/D [83 0 R /XYZ 72 530.7331 null] |
+>> endobj |
+30 0 obj << |
+/D [83 0 R /XYZ 72 259.3549 null] |
+>> endobj |
+82 0 obj << |
+/Font << /F37 57 0 R /F40 63 0 R /F38 60 0 R /F34 77 0 R /F45 74 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+88 0 obj << |
+/Length 1913 |
+/Filter /FlateDecode |
+>> |
+stream |
+OXP½dëq»Å./ÑP8>ȾUÝ´ð¸SV4»D³£^4K[Ñl[ÑLôH èV^v)(ÁþÒ_6B5ß?vw=-àwq°âTqD·ê3¨5çið°s·Ù£j¤FKl¬þõ |
+@H2´¥¶Q3[;ç[¯v A3·mTÉopûTÒ[|òÂçLàãd¶ËÔGÀH8ÐIYMÁÖ{"wÁJ÷ý²Æ&5±2Y){ã |
+ÇÏ.-ÍK¸ãd30°IõEm½ö±ò(jwÐê,§( yÅò0Îãb¦<Ëßb a6tém/Ú³xj$Û9EV5p0/Ëò |
+aWè¹@Ø_Kk¼bûh(uj':uÑÝʪpCªàef3ØAÎrQÎæù8£"*hÕ( |
+EIR¢Nu×9ÏéÊrW¶êõ^"í®ìÖ9ì¯ mçÛ*+È5@î]õa;¤SÐ'¨ÄÚ³iï"tÌ9ܺÇkU[°Ã)ߢÿÛg%JCMé)¬]k9{9Ç+£§´¢ún6½_GÂ×ùôò¦HI§ènVfÝï¿H·Øð0+gãÀD´mE¯ËÎ'».V- ~Uï$æ÷Ìã-.r¬N`óÔµ´¡5²Éc |
+ùEE%Øfì*¬MÔðä½g¥Ù/E·h :êí*g²ä°Ã)ënsÞ§ö2ÙyîÖßö³lÏbo{*&^Ú¢,Ò'¡eûÎ Ç5ðÛel;s`Âå^hÕúitË2R[lk{Ç®lajËNåN8qÅ ©KqÑMÅAú$ö]n=Í;á÷øxYí-Éêh'ÕjNTªä|²©{U-eXWŸJݨöT @¡0ÿ |
+ÁHùÖ>f<ÛVõÂì< |
+Þ¼íØ"Þ»(ç¢ÊëY½Aù¯kïïàôGxö«>¿"¼ßuÿùã¶p§ ¤èÿÛ è>ªÞj½{) ¾¼DO+sñ·ÒNw¼+¦k]üü50ü¼úüîú7iB±äk(Ábâe ©o|RµrèUenù/Ô`õe¬håuð0wø°¿Õq¶¶cNÿá©zñæáÅÿøäùendstream |
+endobj |
+87 0 obj << |
+/Type /Page |
+/Contents 88 0 R |
+/Resources 86 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 67 0 R |
+/Annots [ 90 0 R ] |
+>> endobj |
+90 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 1] |
+/Rect [158.1012 466.8614 248.7616 479.8774] |
+/Subtype/Link/A<</Type/Action/S/URI/URI(http://www.delorie.com/gnu/docs/gdb/gdb_30.html)>> |
+>> endobj |
+89 0 obj << |
+/D [87 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+34 0 obj << |
+/D [87 0 R /XYZ 72 449.9506 null] |
+>> endobj |
+38 0 obj << |
+/D [87 0 R /XYZ 72 231.503 null] |
+>> endobj |
+86 0 obj << |
+/Font << /F34 77 0 R /F40 63 0 R /F37 57 0 R /F38 60 0 R /F45 74 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+93 0 obj << |
+/Length 2910 |
+/Filter /FlateDecode |
+>> |
+stream |
+ß¿Â}èÄ;=óø%j§/IiÒ6ím:ÓIò Ë\[=}8¼{Î__ eÉǽ»|< "!@èó»×_*»¼«Ç©\U &3cW»ïÖßKßÿððõë/u¶à$÷\oeåî72ËÖç±¾ë¦þ©ë¾£OAèìS£µñË?Þo(Öðù ?CR¯·n|v®£ñôúèÊ·4Tî÷§{a×n_a²lûs7ÒtÿHckûÓ%(7¸]~¹ÊW ÕF(éBzåv§úÉ@C-Ößó×̱Wð«uÙ4}ëîhö©.øÂJR1mL6Û¶Èt÷×;Ü>p®dÎ.20º2Ìp¯ªöîÇ»ï~à«Ý_}}Ç*l¶zÎDµwI#M|oîÞÜýk¸$næ"?Çó^ê&sÅL²'>¯¢ßXêØ´dEÁãVúSr¿VÉ·«fÚü·Ûî$ñcÛU8s½Üîɽ¼ße*[ÙG:è®ÄÅy¯É ?æ <cBXû¢i23óßÐ4Ä&ãÓ`È¥iOÎ¥ì"Aªq#H#~h©!Tvh,§ VqCý£!Pc®<E¨8réÏ' ¡hôÝori?¼ÌãXg w?Ëfë 1A8CµÇ+ØÙbã¡ôG-×U98ÿQg:ÐèÞÜ»²½¸²Vëö\æ#rý<|HÈTU}Ûö;o~"®:aß&¥1¢âjDÅ5/¡#>^Dâëó!×ó ïêq ^°·6|ýßûB®û31þÜìç{Îeè}ß5}SWµr`{ ¥Ûº«[ïR |
+ð¾rߺn¤,âjÿ=°ó3£±ðÙo÷ã¡5f,zVM?¸åбzÛ8ú&Ê£w©sËB0,æ&Òa .´úIóáà7Ò÷#ÀK"Ô2Å2v*yL|]Bn*÷á¥;·[o )óÁó輺ÞÁ)r8ÎÀ?¸ªïv,äACkÂËxØ Ï¯õÔßÊ&sÛ'8ÂÞÉ[ÓûS[vUC®85°\ÚOø¨'ªE·\)«j ¥Ñ¬´Ø&R[ºi{¯»{´}Æxftú*°¨Ó ñË2QL"üÏ1À¯Î¥3®Þ¦Óc.Osç>Äbc±5UZ$ªÛEîs-´7d+H¡@_Úù.ÄÒmöñç0dY<@Àäo² |
+`²\P½W0¨æ@q]äÌà|BQ ,¸üõR¸Kô5ÁRßialÊÎÔ{ý¥Ì@çÂpÏ3õíF°\åJÃ#$ñ}»B |
+j¼t®G)/e^¥XÎytoSR¡<DcOÉ""P!¬ U2ð ÔL ýò!¹Ê¿È_\n XUèÂor= Ùåz?ãnTCdG?¾2Gý²ßSÆ<jKÔÀUA f ®>]B@ ò¸Æ3Ïä:@<ÃeÆâá2®]N±så¹Ãè Nn`ÓV¯V2cYÁ½$³væ&¥-`KW¾¸Ö<{Ѿ3Þ/÷MÄïcùÔ£uÿSð{ÞàB=â©lÎnÊ |
+Ö~6` qï® r¡×ɳ&¶½p#´½Ê 9;ðç¨DI¢pEç87%FpJw¸otÈôå¾nËýgGo!D'(m(´Ïo©¸U¤t«Âå~ÃRê}µÃ;îå^ákÂ0²vYá@tb ë¦!lTBÙpÉ |
+Y,Ë ¡Ü$]@ :Û\}Ðq9³*³ÒâàC¥C®üXé (¦þÌË¥C¾(¶8ØI4ÒªVÉÍnÓ<ZMpþû¤ èÙhß@ÑÎîO¬ sñÇÔºñTW1Â,$άXºB8R¸6CÁÌÛ³oÁ»5x¶ý0¦í$Yû]ê½TL°S*[n[BZÌò>ÉH@ÈþèÁ .ŧëã¯u°Cò}£o|¼ïc"ÂÂÑU#ÀÆ,z,ÇNÄ{/ÒØòoº |
+y#÷=M|ºw#-ÑzÀ |
+è'¸ñ[woÂibåõP>Ñz2A£öuG$UTHô8¸r禯[<}Jȱé¶*J¿AmcC#Uã98JÜR¡*B·Ê.ò($®=9|ÝÂÛ[_ØÉ0Jq¶/»ktZÛr¦Æ hZíÐ?A×}xmËdc±«Pé³Ø"+Ä|ÉBø%1W)½þ+e'd9áMËÉ pÊ/ |
+Ï#¯òâ |
+®0±õeè86ùN©¹Ð³¤G8ÎæÓá.5gÊñ¦Ûõüýõ¢ñA'¿Èq j(S¿V(U»ê®zëÒ^2θÍÔˬ¡BF |
+Qê¯ÂRð¡ |
+oþóð-mq#û°±#CÖpõ]BÏ[5ç÷Tø×ý®®|iïç½=ñöðþ:¸o[P}ý=_&ï[^×oßy ëÙ9DZy½éÃ4\|æq¢\V¤·=þ\6Ò®6ãÿ5êß{ ØÄß`©¨ÔW@ì9¤¦Tql$ ü¢(Bo$T2øk& |
+Ó*v¤ÿAA]k¨ÂèèÊ¡¸¢F: |
+5Väê e_Aù÷þ8¼¢°¸æÑ ß) Ò>v4ÄTnk×Oýÿ¿Ãendstream |
+endobj |
+92 0 obj << |
+/Type /Page |
+/Contents 93 0 R |
+/Resources 91 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 67 0 R |
+/Annots [ 104 0 R ] |
+>> endobj |
+104 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[1 0 0] |
+/Rect [448.0482 444.4802 454.5239 459.0709] |
+/Subtype /Link |
+/A << /S /GoTo /D (Hfootnote.1) >> |
+>> endobj |
+94 0 obj << |
+/D [92 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+42 0 obj << |
+/D [92 0 R /XYZ 72 162.862 null] |
+>> endobj |
+105 0 obj << |
+/D [92 0 R /XYZ 88.1395 89.8982 null] |
+>> endobj |
+91 0 obj << |
+/Font << /F38 60 0 R /F45 74 0 R /F37 57 0 R /F40 63 0 R /F33 97 0 R /F15 66 0 R /F34 77 0 R /F1 100 0 R /F25 103 0 R /F48 108 0 R /F26 111 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+114 0 obj << |
+/Length 2900 |
+/Filter /FlateDecode |
+>> |
+stream |
+xÚµÙܶñ}¿bßÄÒp ðö¼råpJë¤R0$fÈ,Èm¾>}s«ä'6èÐèß?ÞÜ[qéÛÇÍm®o³T :ÍÛÇú@©»¢(øSÛîî×Ç"¹UQXF¥B |
+ qç·+]yYDö9R9/NÒÅF´êggïLcceut¾:ÎÂ,/ |
+<._D«°T³¡ø³_=(n©ÕÅ|WÖë8Nmû,£WDÀðLmGÓv¤tÓng/<ÀW%}Ðps³ôR¬!ÕѬµ ²Ö¤Z½v©2M¯åøÕ¥Ò Ó¹¡¶¯ÛʲﱱxiØôKwªÕ5z¸ '<m×ØnÏÊÈ ,+a´nèè0¨øö@¼ûæÿ0ìö-ÞDk|ß±Yp!O½úo«ênAhXmø'êÜ;CÞlÛô?lÌ`Ey,êsQ4üï®ä¸äfb±"ÄN qO¶þFÃ4 |
+ÞýZn4Y~OÕ8}} G7¯YR[ðö üÉ^`WH"/cÃÖc³& AqhyºðøB7|å*-³à¯ÃºgeÐGÜ 'C×ù>ið°t`7î`ªÆG«ÑU×± |
+~ÏÅã ÷ªÁÓ^òëÕÕjQìvaåÍîw©kõ,]tuìâ£üÌÛFEÐ#cëÖUãh¾ Ø.èuÏq³~Æ»`B¨r jÀB6îLÅ"p¼q1n1·,âP>ÐýðáqÙ:âôY9§Q×|8=óùéçE>`wà¦ü¢·Cz«üÊÙQÍ8î¿»¿¯ÙïÃÊ Õn |
+µ½ÞݳOºÇפXX4[Ç|C bF¦øÉcÁoS˳OÌ zàOÅÌÁñ+xu³î^îRd!iñí®9Õ tþ8¨@¢ ûªcªpÕÒ§Ûjé|×$öÖh |
+±;r` m× 2b dÏËP¶é¢Y°¿`£zÎ fõDJpBù^&9áÅÙÏZ'ï9 97µ(¸EûzÐ{í2O¡d>bú$©½rVP×[Ë |
+¢`åm\&¡À´U dÞV»ßn~ù5ºo¢Ûo¢0.{T¾%Üiw`o¹w7nþ>3\ÍWç,¿ÇßWI3LÇy¶&¯²><¡ì)Ó[jêPé¼¼hM |
+°ñ$2Ã.,N¢PÇ:]4%î3°_¼mb2$SÃÀÀ¬ß<X|è+³ww»â"ñöÐ)?¥6ÔÒk ü:ln3õ%To9{ØÐxz¸eIù »°bIv_Yç!¹H¤ñn±ÅT dîïÞ âÕ @XYê».?ùÛ"ÑÅm£+>ÀÏ°éÍ*Ú×ñJ°µÍü»§ôùEyêÂRßJnÛ[æä(:b+C5b@Ò)óAjQ^èF°XÁ©ÈONÉz^*ÅÊT ?i¥TuöK;¾ðص۾żHÇNëær¹Ì§§_jÁyE¸ú!sjÂͺE=:(ZWDømÐe18Ñ ,w3.ãNÖøíSªS¥Ãÿ%¤ÒQFzÈñpÑÁ)0wºbå)i\S¦¼RRáEÙû¬CRó7?pnþ¯ XnÈÆÜìBôÖBÿEö¹x7ìq.sy´òë(·µöÝ[1C½ïÞþ¼äÁ/ûnðLø&z1Ä#§ôO":/³%jn |
+"þÅóÒÎ"ÁâVÔª«ÞîüÓÐ÷_ù§²2!Àø&Êfãì8l0MèÎNÌ÷Ejÿri·"U6ÿorc]ÛMØ,n¦ñ?zvÞ9d;c |8SÅ?åsÿ7Ûfendstream |
+endobj |
+113 0 obj << |
+/Type /Page |
+/Contents 114 0 R |
+/Resources 112 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 117 0 R |
+/Annots [ 116 0 R ] |
+>> endobj |
+116 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 1] |
+/Rect [116.4735 387.2483 425.3239 400.2643] |
+/Subtype/Link/A<</Type/Action/S/URI/URI(http://csapp.cs.cmu.edu/public/ics2/code/vm/malloc/mm.c)>> |
+>> endobj |
+115 0 obj << |
+/D [113 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+46 0 obj << |
+/D [113 0 R /XYZ 72 662.2167 null] |
+>> endobj |
+112 0 obj << |
+/Font << /F38 60 0 R /F34 77 0 R /F45 74 0 R /F40 63 0 R /F37 57 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+120 0 obj << |
+/Length 1981 |
+/Filter /FlateDecode |
+>> |
+stream |
+&û[ïpD^$ÛHLÚ¶ÃLnØ.ÝD£ÔÙ;;µý^mwpã²õJf |
+O¾2È´RàjÎÖ[¢vnçѯ-ºõÐÁ°%˱·§ÎÛ&}\iaX=¯ãñlGjv¯ñ®Ãk?Ü2Ç}à®Ð{ :Ý]Éð#ò%*òR%ʳOÁàxûeiòV_0Ù2èChdëlã=Xóv#ìLâÒ¾ |
+ÁÇ3%S#òµè²LÅÍ2&Ke^Fé $DO.*íJIÑQ*Ô%cÎÙ¦ ¿^´¤Cq÷øh°çÓâ> °¯ÁökÌà? ÔkRP¯YP|4ÉÅDû5|L«V Òɦ®IÈtÄb[F -ÓBd7TûþÕõ-¼wr}pÜÐQCÛ¸º AØ¡àJ¥ÅÌàY Q·B"͵úoGfäí<*AVU rddáøJ¥*+¢@ѵJU¨·zà 2¢®üBkd"UJ_kp |
+TBX6¯¶ìÆÅñ ´âDKê0ôÌ nk7 RXâh¨ºXUw|@¢a?m¤I*r¨£l ë"·kîÃ-|vüí1ùO Î |
+NÌHµ»=VÊ!h9ãanGÔÝ" M U\aøÉ'!Ô |
+¶1 Càæû_d6}/z3È |
+N:LD"Hj%7QeÁÛ4\(¾ìTZU¥¿àTUÕì¡wQòÏê"(9 «ÁG×£^fÆk?°^¦0s.Ü 0ØjL4UgO*SÀÒ7#XUsb§ÂF±§kLþÞ»¢Â*ËõD b]C4Õ¾øÔóÐ@ qqõªAi§%½A¾ÕWøw ¡w.N ¹Î°Åá©sHZÜçG¬Ò´ÉÉSè¢K@üCÛ5÷²W¡ÌW°T¥y5\@O8fò{$L¸ìá ã-Pîs;Nwr¦pí)¤úF}äô| ºçZ¤"ãòZ¥cz¾/åÂÜäÚæ' |
+¾MlËóüÚ°ñ/#{7 Èíl_Ç·¾Cx9ªwóÝ w&3ÑË5`ǾYÙÎ÷Ë.ud&òì[!s9ßf¢H¥ÆXú/Agƹ$Äøë <(²åç©Êðjöô#NFEí,«÷¸x ZÒ¹/7ýà×w®4¡¦ep]eKl]·_¸C7 ü~jwí¯tï¿6êz¥ð/rõg¾A?3´c¢À÷«ß-jKÀþE´ö¼«Wlã#õЧ°å' $õ¸FTä ¤lýJI]PCÕuu,-X»c\¹q|| hñ*ò{OñG÷J.n±29¢ëT 6;@5ëÂxYh¹å ê¿ýWw§Âp bçGþ_ä©ÞŶ~i±FëQ¢0|JØø "§@0j9×]©ígbÕKècc¥¥H~ösÔÓåc¦2¹}=£Mw¨_¿<(¡Ó\Á¥ôZÀÕPeÍ'ßýùéÝÿ Û=endstream |
+endobj |
+119 0 obj << |
+/Type /Page |
+/Contents 120 0 R |
+/Resources 118 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 117 0 R |
+>> endobj |
+121 0 obj << |
+/D [119 0 R /XYZ 72 687.1233 null] |
+>> endobj |
+118 0 obj << |
+/Font << /F34 77 0 R /F45 74 0 R /F37 57 0 R /F40 63 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+110 0 obj << |
+/Length1 775 |
+/Length2 1487 |
+/Length3 532 |
+/Length 2053 |
+/Filter /FlateDecode |
+>> |
+stream |
+&ÈüQ& DÀËüæ&" |