Removed GC, merge it into main to save correct commit history

This commit is contained in:
sebastianselander 2023-05-05 15:12:37 +02:00
parent a720b9ffd8
commit 677a200a15
42 changed files with 4 additions and 3238 deletions

View file

@ -1,5 +0,0 @@
LEVEL := ../..
LIBRARYNAME = GC
LOADABLE_MODULE = 1
include $(LEVEL)/Makefile.common

View file

@ -1,16 +0,0 @@
// TODO: include these properly
#include "llvm/CodeGen/GCStrategy.h"
#include "llvm/CodeGen/GCMetadata.h"
#include "llvm/Support/Compiler.h"
using namespace llvm;
namespace {
class LLVM_LIBRARY_VISIBILITY GC : public GCStrategy {
public:
GC() {}
};
GCRegistry::Add<GC>
X("gc", "The bespoken garbage collector.");
}

View file

@ -1,16 +0,0 @@
#include "llvm/CodeGen/GCMetadataPrinter.h"
#include "llvm/Support/Compiler.h"
using namespace llvm;
namespace {
class LLVM_LIBRARY_VISIBILITY GCPrinter : public GCMetadataPrinter {
public:
virtual void beginAssembly(AsmPrinter &AP);
virtual void finishAssembly(AsmPrinter &AP);
};
GCMetadataPrinterRegistry::Add<MyGCPrinter>
X("gc", "The bespoken garbage collector.");
}

View file

@ -1,4 +0,0 @@
define void @f() gc "gc" {
entry:
ret void
}

View file

@ -1,63 +0,0 @@
/// The map for a single function's stack frame. One of these is
/// compiled as constant data into the executable for each function.
///
/// Storage of metadata values is elided if the %metadata parameter to
/// @llvm.gcroot is null.
struct FrameMap {
int NumRoots; //< Number of roots in stack frame. (int32_t)
int NumMeta; //< Number of metadata entries. May be < NumRoots.
const void *Meta[0]; //< Metadata for each root.
};
/// A link in the dynamic shadow stack. One of these is embedded in
/// the stack frame of each function on the call stack.
struct StackEntry {
StackEntry *Next; //< Link to next stack entry (the caller's).
const FrameMap *Map; //< Pointer to constant FrameMap.
void *Roots[0]; //< Stack roots (in-place array).
};
/// The head of the singly-linked list of StackEntries. Functions push
/// and pop onto this in their prologue and epilogue.
///
/// Since there is only a global list, this technique is not threadsafe.
StackEntry *llvm_gc_root_chain;
/// Calls Visitor(root, meta) for each GC root on the stack.
/// root and meta are exactly the values passed to
/// @llvm.gcroot.
///
/// Visitor could be a function to recursively mark live objects. Or it
/// might copy them to another heap or generation.
///
/// @param Visitor A function to invoke for every GC root on the stack.
void visitGCRoots(void (*Visitor)(void **Root, const void *Meta)) {
for (StackEntry *R = llvm_gc_root_chain; R; R = R->Next) {
unsigned i = 0;
// For roots [0, NumMeta), the metadata pointer is in the FrameMap.
for (unsigned e = R->Map->NumMeta; i != e; ++i)
Visitor(&R->Roots[i], R->Map->Meta[i]);
// For roots [NumMeta, NumRoots), the metadata pointer is null.
for (unsigned e = R->Map->NumRoots; i != e; ++i)
Visitor(&R->Roots[i], nullptr);
}
}
// To access the stack map
void traverseStackMap() {
for (auto I = GCFunctionMetadata::roots_begin(), E = GCFunctionMetadata::end(); I != E; ++I) {
GCFunctionInfo *FI = *I;
unsigned FrameSize = FI->getFrameSize();
size_t RootCount = FI->roots_size();
for (GCFunctionInfo::roots_iterator RI = FI->roots_begin(),
RE = FI->roots_end();
RI != RE; ++RI) {
int RootNum = RI->Num;
int RootStackOffset = RI->StackOffset;
Constant *RootMetadata = RI->Metadata;
}
}
}

View file

@ -1,96 +0,0 @@
CC = clang++
CWD = $(shell pwd)
LIB_INCL = -I$(CWD)/include
LIB_SO = -L$(CWD)/lib
LIB_LINK = $(CWD)/lib
CFLAGS = -Wall -Wextra -v -g -std=gnu++20 -stdlib=libc++ -I
VGFLAGS = --leak-check=full --show-leak-kinds=all
STDFLAGS = -std=gnu++20 -stdlib=libc++
WFLAGS = -Wall -Wextra
DBGFLAGS = -g
advance:
$(CC) $(WFLAGS) $(STDFLAGS) tests/advance.cpp -o tests/advance.out
file:
$(CC) $(WFLAGS) $(STDFLAGS) tests/file.cpp -o tests/file.out
heap:
$(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) lib/heap.cpp
h_test: static_lib
rm -f tests/h_test.out
# $(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) tests/h_test.cpp lib/heap.cpp lib/profiler.cpp lib/event.cpp -o tests/h_test.out
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -g -o tests/h_test.out tests/h_test.cpp lib/gcoll.a
h_test_vg: h_test
valgrind $(VGFLAGS) tests/h_test.out
h_test_dbg: h_test
lldb tests/h_test.out launch
linker:
rm -f tests/linker.out
$(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) tests/linker.cpp lib/heap.cpp -o tests/linker.out
linker_vg: linker
valgrind $(VGFLAGS) tests/linker.out
game:
rm -f tests/game.out
$(CC) $(WFLAGS) $(STDFLAGS) $(LIB_INCL) tests/game.cpp lib/heap.cpp lib/profiler.cpp lib/event.cpp -o tests/game.out
wrapper_test:
rm -f lib/event.o lib/profiler.o lib/heap.o lib/coll.a tests/wrapper_test.out
# compile object files
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/event.o lib/event.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/profiler.o lib/profiler.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/heap.o lib/heap.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -g -c -o lib/cheap.o lib/cheap.cpp -fPIC
# compile object files into library
ar rcs lib/gcoll.a lib/event.o lib/profiler.o lib/heap.o lib/cheap.o
clang -stdlib=libc++ $(WFLAGS) $(LIB_INCL) -o tests/wrapper_test.out tests/wrapper_test.c lib/gcoll.a -lstdc++
extern_lib:
# remove old files
rm -f lib/heap.o lib/libheap.so tests/extern_lib.out
# compile heap to object file
$(CC) $(STDFLAGS) -c -fPIC -o lib/heap.o lib/heap.cpp
$(CC) $(STDFLAGS) -shared -o lib/libheap.so lib/heap.o
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -v tests/extern_lib.cpp lib/heap.cpp -o tests/extern_lib.out
$(CC) $(STDFLAGS) $(LIB_INCL) $(LIB_SO) -v -Wall -o tests/extern_lib.out tests/extern_lib.cpp -lheap
LD_LIBRARY_PATH=$(LIB_LINK) tests/extern_lib.out
static_lib:
# remove old files
rm -f lib/event.o lib/profiler.o lib/heap.o lib/gcoll.a tests/extern_lib.out
# compile object files
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/event.o lib/event.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/profiler.o lib/profiler.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/heap.o lib/heap.cpp -fPIC
# create static library
ar r lib/gcoll.a lib/event.o lib/profiler.o lib/heap.o
# create test program
static_lib_test: static_lib
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/extern_lib.out tests/extern_lib.cpp lib/gcoll.a
alloc_free_list: static_lib
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/alloc_fl.out tests/alloc_free_list.cpp lib/gcoll.a
linked_list_test: static_lib
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -o tests/linkedlist.out tests/linkedlist.cpp lib/gcoll.a
wrapper:
# remove old files
rm -f lib/event.o lib/profiler.o lib/heap.o lib/coll.a tests/wrapper.out
# compile object files
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/event.o lib/event.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/profiler.o lib/profiler.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/heap.o lib/heap.cpp -fPIC
$(CC) $(STDFLAGS) $(WFLAGS) $(LIB_INCL) -O3 -c -o lib/cheap.o lib/cheap.cpp -fPIC
# compile object files into library
ar rcs lib/gcoll.a lib/event.o lib/profiler.o lib/heap.o lib/cheap.o
# compile test program wrapper.c with normal clang
clang -stdlib=libc++ $(WFLAGS) $(LIB_INCL) -o tests/wrapper.out tests/wrapper.c lib/gcoll.a -lstdc++

View file

@ -1,21 +0,0 @@
# Benchmarking
free_overlap():
9_000 nodes:
With indexing:
Execution time: 22624 ≈ 22ms ≈ 0s.
Without indexing:
Execution time: 24891 ≈ 24ms ≈ 0s.
90_000 nodes:
With indexing:
Execution time: 693642 ≈ 693ms ≈ 0s.
Without indexing:
Execution time: 712297 ≈ 712ms ≈ 0s.
Linked list test:
50_000 nodes:
With marking all:
Execution time: 13911478 ≈ 13911ms ≈ 13s.
Without marking:
Execution time: 234361 ≈ 234ms ≈ 0s.

View file

@ -1,40 +0,0 @@
# cheap.h & cheap.cpp
A wrapper interface for the class `GC::Heap` for easier use
in LLVM (no nasty namespaces). This interface is relatively
straight-forward and only defines functions to use the already
public functions in the class `GC::Heap`.
The functions are declared in a normal C-style header and
defined as "pure" C-functions. Because the public functions
exposed in `GC::Heap` are static, some of the functions
just call the static functions but are wrapped as C-functions.
For the non-static function `GC::Heap::set_profiler()` and the
singleton get-instance function `GC::Heap::the()` a struct
is used to encapsulate the heap-object. If this library is
compiled with `DEBUG` defined a struct is typedef-ed and
can be used everywhere, otherwise this struct is opaque
and cannot be used explicitly. This struct only contains
a pointer to the heap instance and is called `cheap_t`.
## Functions
`cheap_t *cheap_the()`: Returns an encapsulated singleton
instance. It is encapsulated in an opaque struct as the
instance itself is not meant to be used outside the C++
library.
`void cheap_init()`: Simply calls the `Heap::init()`
function.
`void cheap_dispose()`: Only calls the `Heap::dispose()`
function.
`void *cheap_alloc(unsigned long size)`: Calls `Heap::alloc(size_t size)`
and returns whatever `alloc` returns.
`void cheap_set_profiler(cheap_t *cheap, bool mode)`:
The argument `cheap` is the encapsulated Heap singleton instance.
`mode` is the same as for `Heap::set_profiler(bool mode)`.
For more documentation on functionality, see `src/GC/docs/lib/heap.md`.

View file

@ -1,26 +0,0 @@
# chunk.hpp
A chunk struct object is the basic element of what can be
stored on the heap. When `Heap::alloc` is called a
chunk may be created to represent the space of memory
that was allocated on the heap by `alloc`.
## Members
`bool m_marked`: A boolean flag to mark an object during mark/sweep.
`uintptr_t *const m_start`: A constant pointer pointing to the start
address of the memory space that was allocated.
`const size_t m_size`: The size of the memory space that was allocated.
## Constructors
There are three constructors for a chunk. One regular constructor
and two copy constructors.
`Chunk(size_t size, uintptr_t *start)`: Used for creating new chunks in
`Heap::alloc`.
`Chunk(const Chunk *const c)`: A copy constructor used by the profiler
to store chunk data after the initial chunk is deleted.
`Chunk(const Chunk &c)`: A secondary copy constructor used for debugging.

View file

@ -1,47 +0,0 @@
# event.hpp & event.cpp
An event class used by the profiler to track actions
on the heap.
## Members
`const GCEventType m_type`: The type of event recorded.
`const std::time_t m_timestamp`: The timestamp of the event,
initialized to the current time by `std::time(NULL)`.
`const Chunk *m_chunk`: The chunk an event is related to.
For example, in `alloc` when a new chunk is created, a
new event is recorded with the type of `NewChunk` and
`m_chunk` then contains a copied version of that new chunk.
If an event is not related to a chunk this member is initialized
to a nullptr.
`const size_t m_size`: In an `AllocStart` event, this member
stores the amount of bytes requested to `alloc`. Otherwise
this member is initialized to 0.
## Constructors
`GCEvent(GCEventType type)`: Used for creating events that are
independent of a chunk and size (like `ProfilerDispose`).
`GCEvent(GCEventType type, Chunk *chunk)`: Used for creating events
that are connected to a chunk (like `ChunkMarked`).
`GCEvent(GCEventType type, size_t size)`: Used for creating events
that are related to a size (only `AllocStart`).
## Destructors
`~GCEvent()`: Default destructor and also frees the member
`m_chunk` if it's not the `nullptr`.
## Functions
`GCEventType get_type()`: Getter for the type of the event.
`std::time_t get_time_stamp()`: Getter for the timestamp of
the event.
`const Chunk *get_chunk()`: Getter for the Chunk the event
is related to. The chunk data is constant.
`const char *type_to_string()`: Translates the type of the
event to a string.

View file

@ -1,54 +0,0 @@
# heap.hpp & heap.cpp
## Members
`char *const m_heap`: This is the pointer to the simulated heap which
collection occurs on. It's a byte array with a constant pointer.
`size_t m_size`: The size of bytes that has been allocated on the heap.
`inline static Heap *m_instance`: The singleton instance of Heap. Before
the heap is initialized this is initialized to the null pointer.
`uintptr_t *m_stack_top`: The address of the topmost stack frame which
serves as the stop for scanning the stack. Initialized as the null pointer
but assigned to the correct address in `Heap::init()`.
`bool m_profiler_enable`: The state of the profiler, `true` if the
profiler is enabled, `false` otherwise. It is initialized as `false`.
`std::vector<Chunk *> m_allocated_chunks`: Contains pointers to all
chunks that are allocated on the heap and can be reachable (if
a collection has been triggered previously).
`std::vector<Chunk *> m_freed_chunks`: Contains pointer to
chunks that have been freed, used to try and recycle chunks.
## Constructors
`Heap()`: Default constructor which guarantees to initialize
the `m_heap` pointer and the byte array. Declared private
in accordance with the singleton pattern.
## Destructors
`~Heap()`: Frees the `m_heap` byte array. Declared private
in accordance with the singleton pattern.
## Functions
`static void init()`: Initializes the heap singleton and the member
`m_instance`. Must be called before any calls to `alloc()`.
`static void dispose()`: Disposes the heap singleton which frees
the heap. If the profiler is enabled the profiler is also disposed.
`static void *alloc(size_t size)`: Tries to allocate `size` amount
of bytes on the heap. The allocation is C-style, meaning `alloc()`
returns a `void *` similar to `malloc` and the user should cast
this pointer to an appropriate type. If this function is called with
the argument of 0, it will return the null pointer. This function can throw
runtime errors on two occasions. One if there is not enough memory
on the heap after a collection is triggered, it will throw a runtime
error with the message "Out of memory". The other occasion is when
a collection is triggered and the heap has not been initialized
properly by calling `init()`.
`static void set_profiler(bool mode)`: Enables or disables (`true`
or `false`) the profiler.

View file

@ -1,30 +0,0 @@
# profiler.hpp & profiler.cpp
## Members
`inline static Profiler *m_instance`: The pointer to the profiler
singleton instance.
`std::vector<GCEvent *> m_events`: A vector of events recorded
by the profiler. The contents are always sorted by time.
## Constructors
`Profiler()`: Default constructor, declared private because of
the singleton pattern.
## Destructors
`~Profiler()`: Default destructor, declared private because of
the singleton pattern. This destructor also deletes any events
that were recorded by the profiler to free memory.
## Functions
`static void record(GCEventType type)`: Records an event independent
of a size and a chunk (like `ProfilerDispose`).
`static void record(GCEventType type, size_t size)`: Records an event independent
of a chunk but not a size (only `AllocStart`).
`static void record(GCEventType type, Chunk *chunk)`: Records an event independent
of a size but not a chunk (like `NewChunk`).
`static void dispose()`: Disposes the profiler by dumping a log file of all
events and deleting events to free memory.

View file

@ -1,83 +0,0 @@
# GC library - reference guide
The Heap class is the core of the library and contains all necessary
functions for using the library. This class exposes four public functions
which are `init`, `dispose`, `alloc`, and `set_profiler`.
To use the library, simply include it as `#include "heap.hpp"` and link
it during compilation. Or you can compile it to a static library using
the target `make static_lib` which compiles everything to an .a file.
It can also be compiled to a shared library if necessary with the target
`make shared_lib` which produces an .so file.
## Quick guide
1. If you want a profiler, call `Heap::set_profiler(true)`. Otherwise this can be skipped.
2. Call `Heap::init()` to initialize the heap before using `alloc` (**crucial**).
3. Use `Heap::alloc()` as you want.
4. At program exit, call `Heap::dispose()` to free up all the memory used.
## Functions
### Heap::init()
When using the library, the user has to, at the start of the program,
call the `void init()` function, which initiates the Heap singleton
and the class member `m_stack_top`. **It is crucial** that this
functions is called from the `main` function of the end program,
as `init` uses the intrinsic function `__builtin_frame_address`
to find the address of the **first** stack frame of the end program.
If the function **is not** called from the `main` function
of the end program, it is not guaranteed that the garbage collector
will collect all objects.
The intrinsic function used is technically unsafe for this use,
but during testing it has only shown to segfault for values greater
than the one used in `init`. If you run into a segfault, please
contact the developers.
### Heap::set_profiler(bool mode)
This function is used to enable or disable the profiler connected
to the Heap. The profiler is primarily used for testing, but can
also be used in general to keep track of the programs history.
This function takes a single boolean as an argument to represent
the state of the profiler. `true` means that the profiler is enabled
and `false` means that the profiler is disabled. This function
can theoretically be called at any time during program execution,
but it's probably a bad idea. It is recommended to call this function
before the call to `init` or at least at before the first call to
`alloc`.
### Heap::alloc(size_t size)
The probably most important function in this library. This function
is called to request memory from the "heap". `alloc` takes a single
argument which is a `size_t` (unsigned long) to represent the amount
of bytes to allocate on the heap. The allocation is C-style, meaning
that alloc returns a `void` pointer similar to `malloc`, which
is then supposed to be cast by the user to a proper pointer. When
`alloc` is called and there is already not enough memory left on
the heap to accommodate the request, a collection is triggered
to free up memory for the allocation. Hence the user does not
need to make their own calls to `free` or manually free up memory.
`alloc` can also return a null pointer, if the user requests to
allocate 0 bytes. This is not recommended.
`alloc` can also throw runtime errors in two cases. The first one
is of there is not enough memory on the heap available after
a collection, which in case the allocation cannot complete.
The second case is during a collection, where the function
`collect` throws a runtime error if the heap is not already
initialized by a call to `init`. Calls to `alloc` can technically
take place without properly initializing the heap, but this is
not recommended.
### Heap::dispose()
This function is used to dispose the heap at the program exit.
If the profiler is enabled, it is also disposed from a call
to `dispose`. When the profiler is disposed, a log file is
dumped containing the events on the heap. If the profiler
is disabled, nothing happens to the profiler during `dispose`.
After the profiler is disposed, the heap is deleted which
frees up all the memory used and deletes (hopefully) all
the remaining objects in memory.

View file

@ -1,36 +0,0 @@
#ifndef CHEAP_H
#define CHEAP_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
//#define WRAPPER_DEBUG
#ifdef WRAPPER_DEBUG
typedef struct cheap
{
void *obj;
} cheap_t;
#else
struct cheap;
typedef struct cheap cheap_t;
#endif
#define FuncCallsOnly 0x1E
#define ChunkOpsOnly 0x3E0
cheap_t *cheap_the();
void cheap_init();
void cheap_dispose();
void *cheap_alloc(unsigned long size);
void cheap_set_profiler(cheap_t *cheap, bool mode);
void cheap_profiler_log_options(cheap_t *cheap, unsigned long flag);
#ifdef __cplusplus
}
#endif
#endif /* __CHEAP_H__ */

View file

@ -1,25 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdlib.h>
namespace GC
{
/**
* The basic element of what can be stored on
* the heap. A chunk contains a start address
* on the actual heap, the size of memory that
* is allocated at that address and if the
* chunk is reachable (marked).
*/
struct Chunk
{
bool m_marked {false};
uintptr_t *const m_start {nullptr};
const size_t m_size {0};
Chunk(size_t size, uintptr_t *start) : m_start(start), m_size(size) {}
Chunk(const Chunk *const c) : m_marked(c->m_marked), m_start(c->m_start), m_size(c->m_size) {}
Chunk(const Chunk &c) : m_marked(c.m_marked), m_start(c.m_start), m_size(c.m_size) {}
};
}

View file

@ -1,55 +0,0 @@
#pragma once
#include <ctime>
#include "chunk.hpp"
namespace GC
{
/**
* Types of events that can occur on the heap.
*/
enum GCEventType
{
HeapInit = 1 << 0,
AllocStart = 1 << 1,
CollectStart = 1 << 2,
MarkStart = 1 << 3,
SweepStart = 1 << 4,
ChunkMarked = 1 << 5,
ChunkSwept = 1 << 6,
ChunkFreed = 1 << 7,
NewChunk = 1 << 8,
ReusedChunk = 1 << 9,
ProfilerDispose = 1 << 10,
FreeStart = 1 << 11
};
/**
* Stores metadeta about an event on the heap.
*/
class GCEvent
{
private:
const GCEventType m_type;
const std::time_t m_timestamp {std::time(NULL)};
const Chunk *m_chunk {nullptr};
const size_t m_size {0};
public:
GCEvent(GCEventType type) : m_type(type) {}
GCEvent(GCEventType type, Chunk *chunk) : m_type(type), m_chunk(chunk) {}
GCEvent(GCEventType type, size_t size) : m_type(type), m_size(size) {}
~GCEvent() {
if (m_chunk != nullptr)
delete m_chunk;
}
GCEventType get_type();
std::time_t get_time_stamp();
const Chunk *get_chunk();
size_t get_size();
const char *type_to_string();
};
}

View file

@ -1,102 +0,0 @@
#pragma once
#include <list>
#include <stdlib.h>
#include <vector>
#include "chunk.hpp"
#include "profiler.hpp"
#define HEAP_SIZE 240240240
#define FREE_THRESH (uint)100
#define HEAP_DEBUG
namespace GC
{
/**
* Flags for the collect overlead for conditional
* collection (mark/sweep/free/all).
*/
enum CollectOption
{
MARK = 1 << 0,
SWEEP = 1 << 1,
MARK_SWEEP = 1 << 2,
FREE = 1 << 3,
COLLECT_ALL = 0b1111 // all flags above
};
/**
* The heap class to represent the heap for the
* garbage collection. The heap is a singleton
* instance and can be retrieved by Heap::the()
* inside the heap class. The heap is represented
* by a char array of size 65536 and can enable
* a profiler to track the actions on the heap.
*/
class Heap
{
private:
Heap() : m_heap(static_cast<char *>(malloc(HEAP_SIZE))) {}
~Heap()
{
std::free((char *)m_heap);
}
char *const m_heap;
size_t m_size{0};
char *m_heap_top{nullptr};
// static Heap *m_instance {nullptr};
uintptr_t *m_stack_top{nullptr};
bool m_profiler_enable{false};
std::vector<Chunk *> m_allocated_chunks;
std::vector<Chunk *> m_freed_chunks;
std::list<Chunk *> m_free_list;
static bool profiler_enabled();
// static Chunk *get_at(std::vector<Chunk *> &list, size_t n);
void collect();
void sweep(Heap &heap);
Chunk *try_recycle_chunks(size_t size);
void free(Heap &heap);
void free_overlap(Heap &heap);
void mark(uintptr_t *start, const uintptr_t *end, std::vector<Chunk *> &worklist);
void print_line(Chunk *chunk);
void print_worklist(std::vector<Chunk *> &list);
void mark_step(uintptr_t start, uintptr_t end, std::vector<Chunk *> &worklist);
// Temporary
Chunk *try_recycle_chunks_new(size_t size);
void free_overlap_new(Heap &heap);
public:
/**
* These are the only five functions which are exposed
* as the API for LLVM. At the absolute start of the
* program the developer has to call init() to ensure
* that the address of the topmost stack frame is
* saved as the limit for scanning the stack in collect.
*/
static Heap &the();
static void init();
static void dispose();
static void *alloc(size_t size);
void set_profiler(bool mode);
void set_profiler_log_options(RecordOption flags);
// Stop the compiler from generating copy-methods
Heap(Heap const &) = delete;
Heap &operator=(Heap const &) = delete;
#ifdef HEAP_DEBUG
void collect(CollectOption flags); // conditional collection
void check_init(); // print dummy things
void print_contents(); // print dummy things
void print_allocated_chunks(Heap *heap); // print the contents in m_allocated_chunks
void print_summary();
#endif
};
}

View file

@ -1,71 +0,0 @@
#pragma once
#include <iostream>
#include <vector>
#include <chrono>
#include "chunk.hpp"
#include "event.hpp"
// #define FunctionCallTypes
// #define ChunkOpsTypes
namespace GC {
enum RecordOption
{
FunctionCalls = (GC::AllocStart | GC::CollectStart | GC::MarkStart | GC::SweepStart),
ChunkOps = (GC::ChunkMarked | GC::ChunkSwept | GC::ChunkFreed | GC::NewChunk | GC::ReusedChunk),
AllOps = 0xFFFFFF
};
struct ProfilerEvent
{
uint m_n {1};
const GCEventType m_type;
ProfilerEvent(GCEventType type) : m_type(type) {}
};
class Profiler {
private:
Profiler() {}
~Profiler()
{
for (GCEvent *c : m_events)
delete c;
}
static Profiler &the();
inline static Profiler *m_instance {nullptr};
std::vector<GCEvent *> m_events;
ProfilerEvent *m_last_prof_event {new ProfilerEvent(HeapInit)};
std::vector<ProfilerEvent *> m_prof_events;
RecordOption flags;
std::chrono::microseconds alloc_time {0};
// size_t alloc_counts {0};
std::chrono::microseconds collect_time {0};
// size_t collect_counts {0};
static void record_data(GCEvent *type);
std::ofstream create_file_stream();
std::string get_log_folder();
static void dump_trace();
static void dump_prof_trace();
static void dump_chunk_trace();
// static void dump_trace_short();
// static void dump_trace_full();
static void print_chunk_event(GCEvent *event, char buffer[22]);
static const char *type_to_string(GCEventType type);
public:
static RecordOption log_options();
static void set_log_options(RecordOption flags);
static void record(GCEventType type);
static void record(GCEventType type, size_t size);
static void record(GCEventType type, Chunk *chunk);
static void record(GCEventType type, std::chrono::microseconds time);
static void dispose();
};
}

View file

@ -1,63 +0,0 @@
#include <stdlib.h>
#include <iostream>
#include "heap.hpp"
#include "cheap.h"
#ifndef WRAPPER_DEBUG
struct cheap
{
void *obj;
};
#endif
cheap_t *cheap_the()
{
cheap_t *c;
GC::Heap *heap;
c = static_cast<cheap_t *>(malloc(sizeof(cheap_t)));
heap = &GC::Heap::the();
c->obj = heap;
return c;
}
void cheap_init()
{
GC::Heap::init();
}
void cheap_dispose()
{
std::cout << "In dispose\n";
GC::Heap::dispose();
std::cout << "Out dispose" << std::endl;
}
void *cheap_alloc(unsigned long size)
{
return GC::Heap::alloc(size);
}
void cheap_set_profiler(cheap_t *cheap, bool mode)
{
GC::Heap *heap = static_cast<GC::Heap *>(cheap->obj);
heap->set_profiler(mode);
}
void cheap_profiler_log_options(cheap_t *cheap, unsigned long flags)
{
GC::Heap *heap = static_cast<GC::Heap *>(cheap->obj);
GC::RecordOption cast_flag;
if (flags == FuncCallsOnly)
cast_flag = GC::FunctionCalls;
else if (flags == ChunkOpsOnly)
cast_flag = GC::ChunkOps;
else
cast_flag = GC::AllOps;
heap->set_profiler_log_options(cast_flag);
}

View file

@ -1,71 +0,0 @@
#include "chunk.hpp"
#include "event.hpp"
namespace GC
{
/**
* @returns The type of the event
*/
GCEventType GCEvent::get_type()
{
return m_type;
}
/**
* @returns The time the event happened in
* the form of time_t.
*/
std::time_t GCEvent::get_time_stamp()
{
return m_timestamp;
}
/**
* If the event is related to a chunk, this
* function returns the chunk that it is
* related to. If the event is independent
* of a chunk, it returns the nullptr.
*
* @returns A chunk pointer or the nullptr.
*/
const Chunk *GCEvent::get_chunk()
{
return m_chunk;
}
/**
* If the event is an AllocStart event, this
* returns the size of the alloc() request.
* otherwise this returns 0.
*
* @returns A number representing the number
* of bytes requested to alloc()
* or 0 if the event is not an
* AllocStart event.
*/
size_t GCEvent::get_size()
{
return m_size;
}
/**
* @returns The string conversion of the event type.
*/
const char *GCEvent::type_to_string()
{
switch (m_type)
{
case HeapInit: return "HeapInit";
case AllocStart: return "AllocStart";
case CollectStart: return "CollectStart";
case MarkStart: return "MarkStart";
case ChunkMarked: return "ChunkMarked";
case ChunkSwept: return "ChunkSwept";
case ChunkFreed: return "ChunkFreed";
case NewChunk: return "NewChunk";
case ReusedChunk: return "ReusedChunk";
case ProfilerDispose: return "ProfilerDispose";
default: return "[Unknown]";
}
}
}

Binary file not shown.

View file

@ -1,671 +0,0 @@
#include <iostream>
#include <stdexcept>
#include <stdlib.h>
#include <vector>
#include <chrono>
#include "heap.hpp"
#define time_now std::chrono::high_resolution_clock::now()
#define to_us std::chrono::duration_cast<std::chrono::microseconds>
using std::cout, std::endl, std::vector, std::hex, std::dec;
namespace GC
{
/**
* This implementation of the() guarantees laziness
* on the instance and a correct destruction with
* the destructor.
*
* @returns The singleton object.
*/
Heap& Heap::the()
{
static Heap instance;
return instance;
}
/**
* Initialises the heap singleton and saves the address
* of the calling function's stack frame as the stack_top.
* Presumeably this address points to the stack frame of
* the compiled LLVM executable after linking.
*/
void Heap::init()
{
Heap &heap = Heap::the();
if (heap.profiler_enabled())
Profiler::record(HeapInit);
// clang complains because arg for __b_f_a is not 0 which is "unsafe"
#pragma clang diagnostic ignored "-Wframe-address"
heap.m_stack_top = static_cast<uintptr_t *>(__builtin_frame_address(1));
heap.m_heap_top = heap.m_heap;
}
void Heap::set_profiler_log_options(RecordOption flags)
{
Profiler::set_log_options(flags);
}
/**
* Disposes the heap and the profiler at program exit
* which also triggers a heap log file dumped if the
* profiler is enabled.
*/
void Heap::dispose()
{
Heap &heap = Heap::the();
if (heap.profiler_enabled())
Profiler::dispose();
}
/**
* Allocates a given amount of bytes on the heap.
*
* @param size The amount of bytes to be allocated.
*
* @return A pointer to the address where the memory
* has been allocated. This pointer is supposed
* to be casted to and object pointer.
*/
void *Heap::alloc(size_t size)
{
auto a_start = time_now;
// Singleton
Heap &heap = Heap::the();
bool profiler_enabled = heap.profiler_enabled();
if (profiler_enabled)
Profiler::record(AllocStart, size);
if (size == 0)
{
cout << "Heap: Cannot alloc 0B. No bytes allocated." << endl;
return nullptr;
}
if (heap.m_size + size > HEAP_SIZE)
{
// auto a_ms = to_us(c_start - a_start);
// Profiler::record(AllocStart, a_ms);
heap.collect();
// If memory is not enough after collect, crash with OOM error
if (heap.m_size + size > HEAP_SIZE)
{
if (profiler_enabled)
Profiler::dispose();
throw std::runtime_error(std::string("Error: Heap out of memory"));
}
}
// If a chunk was recycled, return the old chunk address
Chunk *reused_chunk = heap.try_recycle_chunks(size);
if (reused_chunk != nullptr)
{
if (profiler_enabled)
Profiler::record(ReusedChunk, reused_chunk);
auto a_end = time_now;
auto a_ms = to_us(a_end - a_start);
Profiler::record(AllocStart, a_ms);
return static_cast<void *>(reused_chunk->m_start);
}
// If no free chunks was found (reused_chunk is a nullptr),
// then create a new chunk
auto new_chunk = new Chunk(size, (uintptr_t *)(heap.m_heap + heap.m_size));
heap.m_size += size;
heap.m_allocated_chunks.push_back(new_chunk);
if (profiler_enabled)
Profiler::record(NewChunk, new_chunk);
auto a_end = time_now;
auto a_ms = to_us(a_end - a_start);
Profiler::record(AllocStart, a_ms);
return new_chunk->m_start;
}
/**
* Tries to recycle used and freed chunks that are
* already allocated objects by the OS but freed
* from our Heap. This reduces the amount of GC
* objects slightly which saves time from malloc'ing
* memory from the OS.
*
* @param size Amount of bytes needed for the object
* which is about to be allocated.
*
* @returns If a chunk is found and recycled, a
* pointer to the allocated memory for
* the object is returned. If not, a
* nullptr is returned to signify no
* chunks were found.
*/
Chunk *Heap::try_recycle_chunks(size_t size)
{
Heap &heap = Heap::the();
// Check if there are any freed chunks large enough for current request
for (size_t i = 0; i < heap.m_freed_chunks.size(); i++)
{
//auto chunk = Heap::get_at(heap.m_freed_chunks, i);
auto chunk = heap.m_freed_chunks[i];
auto iter = heap.m_freed_chunks.begin();
advance(iter, i);
if (chunk->m_size > size)
{
// Split the chunk, use one part and add the remaining part to
// the list of freed chunks
size_t diff = chunk->m_size - size;
auto chunk_complement = new Chunk(diff, chunk->m_start + chunk->m_size);
heap.m_freed_chunks.erase(iter);
heap.m_freed_chunks.push_back(chunk_complement);
heap.m_allocated_chunks.push_back(chunk);
return chunk;
}
else if (chunk->m_size == size)
{
// Reuse the whole chunk
heap.m_freed_chunks.erase(iter);
heap.m_allocated_chunks.push_back(chunk);
return chunk;
}
}
// If no chunk was found, return nullptr
return nullptr;
}
/**
* Returns a bool whether the profiler is enabled
* or not.
*
* @returns True or false if the profiler is enabled
* or disabled respectively.
*/
bool Heap::profiler_enabled() {
Heap &heap = Heap::the();
return heap.m_profiler_enable;
}
/**
* Collection phase of the garbage collector. When
* an allocation is requested and there is no space
* left on the heap, a collection is triggered. This
* function is private so that the user cannot trigger
* a collection unneccessarily.
*/
void Heap::collect()
{
auto c_start = time_now;
Heap &heap = Heap::the();
if (heap.profiler_enabled())
Profiler::record(CollectStart);
// get current stack frame
auto stack_bottom = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
if (heap.m_stack_top == nullptr)
throw std::runtime_error(std::string("Error: Heap is not initialized, read the docs!"));
uintptr_t *stack_top = heap.m_stack_top;
auto work_list = heap.m_allocated_chunks;
mark(stack_bottom, stack_top, work_list);
sweep(heap);
free(heap);
auto c_end = time_now;
Profiler::record(CollectStart, to_us(c_end - c_start));
}
/**
* Iterates through the stack, if an element on the stack points to a chunk,
* called a root chunk, that chunk is marked (i.e. reachable).
* Then it recursively follows all chunks which are possibly reachable from
* the root chunk and mark those chunks.
* If a chunk is marked it is removed from the worklist, since it's no longer of
* concern for this method.
*
* Time complexity: 0(N^2 * log(N)) as upper bound.
* Where N is either the size of the worklist or the size of
* the stack frame, depending on which is the largest.
*
* @param start Pointer to the start of the stack frame.
* @param end Pointer to the end of the stack frame.
* @param worklist The currently allocated chunks, which haven't been marked.
*/
void Heap::mark(uintptr_t *start, const uintptr_t* const end, vector<Chunk *> &worklist)
{
Heap &heap = Heap::the();
bool profiler_enabled = heap.m_profiler_enable;
if (profiler_enabled)
Profiler::record(MarkStart);
// To find adresses thats in the worklist
for (; start <= end; start++)
{
auto it = worklist.begin();
auto stop = worklist.end();
while (it != stop)
{
Chunk *chunk = *it;
auto c_start = reinterpret_cast<uintptr_t>(chunk->m_start);
auto c_size = reinterpret_cast<uintptr_t>(chunk->m_size);
auto c_end = reinterpret_cast<uintptr_t>(c_start + c_size);
// Check if the stack pointer points to something within the chunk
if (c_start <= *start && *start < c_end)
{
if (!chunk->m_marked)
{
if (profiler_enabled)
Profiler::record(ChunkMarked, chunk);
chunk->m_marked = true;
it = worklist.erase(it);
// Recursively call mark, to see if the reachable chunk further points to another chunk
mark((uintptr_t *)c_start, (uintptr_t *)c_end, worklist);
}
else
{
++it;
}
}
else
{
++it;
}
}
}
}
/**
* Sweeps the heap, unmarks the marked chunks for the next cycle,
* adds the unmarked nodes to the list of freed chunks; to be freed.
*
* Time complexity: O(N^2), where N is the number of allocated chunks.
* It is quadratic, in the worst case,
* since each call to erase() is linear.
*
* @param heap Pointer to the heap singleton instance.
*/
void Heap::sweep(Heap &heap)
{
bool profiler_enabled = heap.m_profiler_enable;
if (profiler_enabled)
Profiler::record(SweepStart);
auto iter = heap.m_allocated_chunks.begin();
// This cannot "iter != stop", results in seg fault, since the end gets updated, I think.
while (iter != heap.m_allocated_chunks.end())
{
Chunk *chunk = *iter;
// Unmark the marked chunks for the next iteration.
if (chunk->m_marked)
{
chunk->m_marked = false;
++iter;
}
else
{
// Add the unmarked chunks to freed chunks and remove from
// the list of allocated chunks
if (profiler_enabled)
Profiler::record(ChunkSwept, chunk);
heap.m_freed_chunks.push_back(chunk);
iter = heap.m_allocated_chunks.erase(iter);
heap.m_size -= chunk->m_size;
}
}
}
/**
* Frees chunks that was moved to the list m_freed_chunks
* by the sweep phase. If there are more than a certain
* amount of free chunks, delete the free chunks to
* avoid cluttering.
*
* Time complexity: O(N^2), where N is the freed chunks.
* If free_overlap() is called, it runs in O(N^2),
* otherwise O(N).
*
* @param heap Heap singleton instance, only for avoiding
* redundant calls to the singleton get
*/
void Heap::free(Heap &heap)
{
bool profiler_enabled = heap.m_profiler_enable;
if (profiler_enabled)
Profiler::record(FreeStart);
if (heap.m_freed_chunks.size() > FREE_THRESH)
{
bool profiler_enabled = heap.profiler_enabled();
while (heap.m_freed_chunks.size())
{
auto chunk = heap.m_freed_chunks.back();
heap.m_freed_chunks.pop_back();
if (profiler_enabled)
Profiler::record(ChunkFreed, chunk);
delete chunk;
}
}
// if there are chunks but not more than FREE_THRESH
else if (heap.m_freed_chunks.size())
{
// essentially, always check for overlap between
// chunks before finishing the allocation
free_overlap(heap);
}
}
/**
* Checks for overlaps between freed chunks of memory
* and removes overlapping chunks while prioritizing
* the chunks at lower addresses.
*
* Time complexity: O(N^2), where N is the number of freed chunks.
* At each iteration get_at() is called, which is linear.
*
* @param heap Heap singleton instance, only for avoiding
* redundant calls to the singleton get
*
* @note Maybe this should be changed to prioritizing
* larger chunks. Should remove get_at() to indexing,
* since that's constant.
*/
void Heap::free_overlap(Heap &heap) // borde göra en record(ChunkFreed) på onödiga chunks
{
std::vector<Chunk *> filtered;
size_t i = 0;
//auto prev = Heap::get_at(heap.m_freed_chunks, i++);
auto prev = heap.m_freed_chunks[i++];
prev->m_marked = true;
filtered.push_back(prev);
cout << filtered.back()->m_start << endl;
for (; i < heap.m_freed_chunks.size(); i++)
{
prev = filtered.back();
//auto next = Heap::get_at(heap.m_freed_chunks, i);
auto next = heap.m_freed_chunks[i];
auto p_start = (uintptr_t)(prev->m_start);
auto p_size = (uintptr_t)(prev->m_size);
auto n_start = (uintptr_t)(next->m_start);
if (n_start >= (p_start + p_size))
{
next->m_marked = true;
filtered.push_back(next);
}
}
heap.m_freed_chunks.swap(filtered);
bool profiler_enabled = heap.m_profiler_enable;
// After swap m_freed_chunks contains still available chunks
// and filtered contains all the chunks, so delete unused chunks
for (Chunk *chunk : filtered)
{
// if chunk was filtered away, delete it
if (!chunk->m_marked)
{
if (profiler_enabled)
Profiler::record(ChunkFreed, chunk);
delete chunk;
}
else
{
chunk->m_marked = false;
}
}
}
void Heap::set_profiler(bool mode)
{
Heap &heap = Heap::the();
heap.m_profiler_enable = mode;
}
#ifdef HEAP_DEBUG
/**
* Prints the result of Heap::init() and a dummy value
* for the current stack frame for reference.
*/
void Heap::check_init()
{
Heap &heap = Heap::the();
cout << "Heap addr:\t" << &heap << "\n";
cout << "GC m_stack_top:\t" << heap.m_stack_top << "\n";
auto stack_bottom = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
cout << "GC stack_bottom:\t" << stack_bottom << endl;
}
/**
* Conditional collection, only to be used in debugging
*
* @param flags Bitmap of flags
*/
void Heap::collect(CollectOption flags)
{
set_profiler(true);
Heap &heap = Heap::the();
if (heap.m_profiler_enable)
Profiler::record(CollectStart);
cout << "DEBUG COLLECT\nFLAGS: ";
if (flags & MARK)
cout << "\n - MARK";
if (flags & SWEEP)
cout << "\n - SWEEP";
if (flags & FREE)
cout << "\n - FREE";
cout << "\n";
// get the frame adress, whwere local variables and saved registers are located
auto stack_bottom = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
cout << "Stack bottom in collect:\t" << stack_bottom << "\n";
uintptr_t *stack_top = heap.m_stack_top;
cout << "Stack end in collect:\t " << stack_top << endl;
auto work_list = heap.m_allocated_chunks;
if (flags & MARK)
mark(stack_bottom, stack_top, work_list);
if (flags & SWEEP)
sweep(heap);
if (flags & FREE)
free(heap);
}
// Mark child references from the root references
void mark_test(vector<Chunk *> &worklist)
{
while (worklist.size() > 0)
{
Chunk *ref = worklist.back();
worklist.pop_back();
Chunk *child = (Chunk *)ref; // this is probably not correct
if (child != nullptr && !child->m_marked)
{
child->m_marked = true;
worklist.push_back(child);
mark_test(worklist);
}
}
}
// Mark the root references and look for child references to them
void mark_from_roots(uintptr_t *start, const uintptr_t *end)
{
vector<Chunk *> worklist;
for (; start > end; start--)
{
if (*start % 8 == 0)
{ // all pointers must be aligned as double words
Chunk *ref = (Chunk *)*start;
if (ref != nullptr && !ref->m_marked)
{
ref->m_marked = true;
worklist.push_back(ref);
mark_test(worklist);
}
}
}
}
// For testing purposes
void Heap::print_line(Chunk *chunk)
{
cout << "Marked: " << chunk->m_marked << "\nStart adr: " << chunk->m_start << "\nSize: " << chunk->m_size << " B\n"
<< endl;
}
void Heap::print_worklist(std::vector<Chunk *> &list)
{
for (auto cp : list)
cout << "Chunk at:\t" << cp->m_start << "\nSize:\t\t" << cp->m_size << "\n";
cout << endl;
}
void Heap::print_contents()
{
Heap &heap = Heap::the();
if (heap.m_allocated_chunks.size())
{
cout << "\nALLOCATED CHUNKS #" << dec << heap.m_allocated_chunks.size() << endl;
for (auto chunk : heap.m_allocated_chunks)
print_line(chunk);
}
else
{
cout << "NO ALLOCATIONS\n" << endl;
}
if (heap.m_freed_chunks.size())
{
cout << "\nFREED CHUNKS #" << dec << heap.m_freed_chunks.size() << endl;
for (auto fchunk : heap.m_freed_chunks)
print_line(fchunk);
}
else
{
cout << "NO FREED CHUNKS" << endl;
}
}
void Heap::print_summary()
{
Heap &heap = Heap::the();
if (heap.m_allocated_chunks.size())
{
cout << "\nALLOCATED CHUNKS #" << dec << heap.m_allocated_chunks.size() << endl;
}
else
{
cout << "NO ALLOCATIONS\n" << endl;
}
if (heap.m_freed_chunks.size())
{
cout << "\nFREED CHUNKS #" << dec << heap.m_freed_chunks.size() << endl;
}
else
{
cout << "NO FREED CHUNKS" << endl;
}
}
void Heap::print_allocated_chunks(Heap *heap) {
cout << "--- Allocated Chunks ---\n" << endl;
for (auto chunk : heap->m_allocated_chunks) {
print_line(chunk);
}
}
Chunk *Heap::try_recycle_chunks_new(size_t size)
{
Heap &heap = Heap::the();
// Check if there are any freed chunks large enough for current request
for (size_t i = 0; i < heap.m_freed_chunks.size(); i++)
{
auto chunk = heap.m_freed_chunks[i]; //Heap::get_at(heap.m_freed_chunks, i);
auto iter = heap.m_freed_chunks.begin();
//advance(iter, i);
i++;
if (chunk->m_size > size)
{
// Split the chunk, use one part and add the remaining part to
// the list of freed chunks
size_t diff = chunk->m_size - size;
auto chunk_complement = new Chunk(diff, chunk->m_start + chunk->m_size);
heap.m_freed_chunks.erase(iter);
heap.m_freed_chunks.push_back(chunk_complement);
heap.m_allocated_chunks.push_back(chunk);
return chunk;
}
else if (chunk->m_size == size)
{
// Reuse the whole chunk
heap.m_freed_chunks.erase(iter);
heap.m_allocated_chunks.push_back(chunk);
return chunk;
}
}
// If no chunk was found, return nullptr
return nullptr;
}
void Heap::free_overlap_new(Heap &heap) // borde göra en record(ChunkFreed) på onödiga chunks
{
std::vector<Chunk *> filtered;
size_t i = 0;
auto prev = heap.m_freed_chunks[i++]; //Heap::get_at(heap.m_freed_chunks, i++);
prev->m_marked = true;
filtered.push_back(prev);
cout << filtered.back()->m_start << endl;
for (; i < heap.m_freed_chunks.size(); i++)
{
prev = filtered.back();
auto next = heap.m_freed_chunks[i]; //Heap::get_at(heap.m_freed_chunks, i);
auto p_start = (uintptr_t)(prev->m_start);
auto p_size = (uintptr_t)(prev->m_size);
auto n_start = (uintptr_t)(next->m_start);
if (n_start >= (p_start + p_size))
{
next->m_marked = true;
filtered.push_back(next);
}
}
heap.m_freed_chunks.swap(filtered);
bool profiler_enabled = heap.m_profiler_enable;
// After swap m_freed_chunks contains still available chunks
// and filtered contains all the chunks, so delete unused chunks
for (Chunk *chunk : filtered)
{
// if chunk was filtered away, delete it
if (!chunk->m_marked)
{
if (profiler_enabled)
Profiler::record(ChunkFreed, chunk);
delete chunk;
}
else
{
chunk->m_marked = false;
}
}
}
#endif
}

View file

@ -1,311 +0,0 @@
#include <ctime>
#include <cstring>
#include <iostream>
#include <fstream>
#include <time.h>
#include <vector>
#include <unistd.h>
#include <stdexcept>
#include "chunk.hpp"
#include "event.hpp"
#include "profiler.hpp"
// #define MAC_OS
namespace GC
{
Profiler& Profiler::the()
{
static Profiler instance;
return instance;
}
RecordOption Profiler::log_options()
{
Profiler &prof = Profiler::the();
return prof.flags;
}
void Profiler::set_log_options(RecordOption flags)
{
Profiler &prof = Profiler::the();
prof.flags = flags;
}
void Profiler::record_data(GCEvent *event)
{
Profiler &prof = Profiler::the();
prof.m_events.push_back(event);
if (prof.m_last_prof_event->m_type == event->get_type())
prof.m_last_prof_event->m_n++;
else
{
prof.m_prof_events.push_back(prof.m_last_prof_event);
prof.m_last_prof_event = new ProfilerEvent(event->get_type());
}
}
/**
* Records an event independent of a chunk.
*
* @param type The type of event to record.
*/
void Profiler::record(GCEventType type)
{
Profiler &prof = Profiler::the();
if (prof.flags & type)
Profiler::record_data(new GCEvent(type));
// auto event = new GCEvent(type);
// auto profiler = Profiler::the();
// profiler.m_events.push_back(event);
}
/**
* This overload is only used with an AllocStart
* event.
*
* @param type The type of event to record.
*
* @param size The size of requested to alloc().
*/
void Profiler::record(GCEventType type, size_t size)
{
Profiler &prof = Profiler::the();
if (prof.flags & type)
Profiler::record_data(new GCEvent(type, size));
// auto event = new GCEvent(type, size);
// auto profiler = Profiler::the();
// profiler.m_events.push_back(event);
}
void Profiler::dump_trace()
{
Profiler &prof = Profiler::the();
if (prof.flags & FunctionCalls)
dump_prof_trace();
else
dump_chunk_trace();
}
/**
* Records an event related to a chunk.
*
* @param type The type of event to record.
*
* @param chunk The chunk the event is connected
* to.
*/
void Profiler::record(GCEventType type, Chunk *chunk)
{
// Create a copy of chunk to store in the profiler
// because in free() chunks are deleted and cannot
// be referenced by the profiler. These copied
// chunks are deleted by the profiler on dispose().
Profiler &prof = Profiler::the();
if (prof.flags & type)
{
auto chunk_copy = new Chunk(chunk);
auto event = new GCEvent(type, chunk_copy);
Profiler::record_data(event);
}
// auto profiler = Profiler::the();
// profiler.m_events.push_back(event);
}
void Profiler::record(GCEventType type, std::chrono::microseconds time)
{
Profiler &prof = Profiler::the();
if (type == AllocStart)
{
prof.alloc_time += time;
}
else if (type == CollectStart)
{
prof.collect_time += time;
}
}
void Profiler::dump_prof_trace()
{
Profiler &prof = Profiler::the();
prof.m_prof_events.push_back(prof.m_last_prof_event);
auto start = prof.m_prof_events.begin();
auto end = prof.m_prof_events.end();
int allocs = 0, collects = 0;
char buffer[22];
std::ofstream fstr = prof.create_file_stream();
while (start != end)
{
auto event = *start++;
if (event->m_type == AllocStart)
allocs += event->m_n;
else if (event->m_type == CollectStart)
collects += event->m_n;
fstr << "\n--------------------------------\n"
<< Profiler::type_to_string(event->m_type) << " "
<< event->m_n << " times:";
}
fstr << "\n--------------------------------";
fstr << "\n\nTime spent on allocations:\t" << prof.alloc_time.count() << " microseconds"
<< "\nAllocation cycles:\t" << allocs
<< "\nTime spent on collections:\t" << prof.collect_time.count() << " microseconds"
<< "\nCollection cycles:\t" << collects
<< "\n--------------------------------";
}
/**
* Prints the history of the recorded events
* to a log file in the /tests/logs folder.
*/
void Profiler::dump_chunk_trace()
{
Profiler &prof = Profiler::the();
auto start = prof.m_events.begin();
auto end = prof.m_events.end();
// Buffer for timestamp
char buffer[22];
while (start != end)
{
auto event = *start++;
auto e_type = event->get_type();
prof.print_chunk_event(event, buffer);
}
}
void Profiler::print_chunk_event(GCEvent *event, char buffer[22])
{
Profiler &prof = Profiler::the();
// File output stream
std::ofstream fstr = prof.create_file_stream();
std::time_t tt = event->get_time_stamp();
std::tm *btm = std::localtime(&tt);
std::strftime(buffer, 22, "%a %T", btm);
fstr << "--------------------------------\n"
<< buffer
<< "\nEvent:\t" << Profiler::type_to_string(event->get_type());
// event->type_to_string();
const Chunk *chunk = event->get_chunk();
if (event->get_type() == AllocStart)
{
fstr << "\nSize: " << event->get_size();
}
else if (chunk)
{
fstr << "\nChunk: " << chunk->m_start
<< "\n Size: " << chunk->m_size
<< "\n Mark: " << chunk->m_marked;
}
fstr << "\n";
}
/**
* Deletes the profiler singleton and all
* the events recorded after recording
* the ProfilerDispose event and dumping
* the history to a log file.
*/
void Profiler::dispose()
{
Profiler::record(ProfilerDispose);
Profiler::dump_trace();
}
/**
* Creates a filestream for the future
* log file to print the history to in
* dump_trace().
*
* @returns The output stream to the file.
*/
std::ofstream Profiler::create_file_stream()
{
// get current time
std::time_t tt = std::time(NULL);
std::tm *ptm = std::localtime(&tt);
// format to string
char buffer[32];
std::strftime(buffer, 32, "/log_%a_%H_%M_%S.txt", ptm);
std::string filename(buffer);
// const std::string ABS_PATH = "/home/virre/dev/systemF/org/language/src/GC/";
// // const std::string ABS_PATH = "/Users/valtermiari/Desktop/DV/Bachelors/code/language/src/GC";
// std::string fullpath = ABS_PATH + filename;
const std::string fullpath = get_log_folder() + filename;
std::ofstream fstr(fullpath);
return fstr;
}
/**
* This function retrieves the path to the folder
* of the executable to use for log files.
*
* @returns The path to the logs folder.
*
* @throws A runtime error if the call
* to readlink() fails.
*/
std::string Profiler::get_log_folder()
{
#ifndef MAC_OS
char buffer[1024];
// chars read from path
ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer)-1);
// if readlink fails
if (len == -1)
{
throw std::runtime_error(std::string("Error: readlink failed on '/proc/self/exe/'"));
}
buffer[len] = '\0';
// convert to string for string operators
auto path = std::string(buffer);
// remove filename
size_t last_slash = path.find_last_of('/');
std::string folder = path.substr(0, last_slash);
#else
auto folder = std::string("/Users/valtermiari/Desktop/DV/Bachelors/code/language/src/GC/tests");
#endif
return folder + "/logs";
}
const char *Profiler::type_to_string(GCEventType type)
{
switch (type)
{
case HeapInit: return "HeapInit";
case AllocStart: return "AllocStart";
case CollectStart: return "CollectStart";
case MarkStart: return "MarkStart";
case ChunkMarked: return "ChunkMarked";
case ChunkSwept: return "ChunkSwept";
case ChunkFreed: return "ChunkFreed";
case NewChunk: return "NewChunk";
case ReusedChunk: return "ReusedChunk";
case ProfilerDispose: return "ProfilerDispose";
case SweepStart: return "SweepStart";
case FreeStart: return "FreeStart";
default: return "[Unknown]";
}
}
}

View file

@ -1,87 +0,0 @@
#include <iostream>
#include <vector>
#define HEAP_SIZE 65536 // Arbitrary for now, 2^16
using namespace std;
/* A simple mark and sweep algorithm */
// Shouldn't be exposed. For now, it is
struct ObjectHeader {
size_t size = sizeof(this);
bool marked = false;
};
struct Object : ObjectHeader {
char name; // should be something like id, but for testing sake its char
Object* child;
// Object(char name_) {}
Object(char name_, Object* child_) {
name = name_;
child = child_;
}
};
// Representing the heap as a simple struct for now
struct Heap {
Object heap_space[HEAP_SIZE];
};
// For now it assumes that it is given root objects from the start, no root finding included
class MarkSweep {
public:
void mark(Object* obj) {
if (!markedBit(obj)) {
markBit(obj);
Object* ref = obj->child;
if (ref != nullptr) {
mark(ref);
}
}
}
void sweep(vector<Object*> worklist) {
for (Object* obj: worklist) {
if (!markedBit(obj) && obj != nullptr) {
delete obj;
}
}
}
private:
bool markedBit(Object* obj) {
return obj->marked;
}
void markBit(Object* obj) {
obj->marked = true;
}
};
int main() {
Object* b = new Object('B', nullptr);
// b->name = 'B';
// b->child = nullptr;
Object* c = new Object('C', b);
// c->name = 'C';
// c->child = b; // c -> d
Object* d = new Object('D', nullptr);
// d->name = 'D';
// d->child = nullptr;
//Heap* heap = new Heap{*c, *b, *d};
vector<Object*> worklist = {c, b, d};
MarkSweep* gc = new MarkSweep();
gc->mark(c);
cout << "Expected 1, got: " << b->marked << '\n';
cout << "Expected 1, got: " << c->marked << '\n';
cout << "Expected 0, got: " << d->marked << '\n';
gc->sweep(worklist);
cout << b->name << '\n';
cout << c->name << '\n';
cout << d->name << '\n'; // The object at d is now deleted (freed)
return 0;
}

View file

@ -1,83 +0,0 @@
#include <chrono>
#include <cstring>
#include <iostream>
#include <list>
#include <time.h>
#include <stdlib.h>
// void time_test()
// {
// using TimeStamp = std::chrono::_V2::system_clock::time_point;
// std::list<char> l;
// char c = 'a';
// for (int i = 1; i <= 5; i++) {
// l.push_back(c++);
// }
// auto iter = l.begin();
// auto stop = l.end();
// while (iter != stop) {
// std::cout << *iter << " ";
// iter++;
// }
// std::cout << std::endl;
// iter = l.begin();
// while (*iter != *stop) {
// std::cout << *iter << " ";
// iter++;
// }
// std::cout << std::endl;
// std::cout << "rebased" << std::endl;
// std::cout << "iter: " << *iter << "\nstop: " << *stop << std::endl;
// TimeStamp ts = std::chrono::system_clock::now();
// std::time_t tt = std::chrono::system_clock::to_time_t(ts);
// std::string tstr = std::ctime(&tt);
// tstr.resize(tstr.size()-1);
// std::cout << tstr << std::endl;
// }
void iter_test()
{
std::list<int> list;
list.push_back(1);
list.push_back(2);
list.push_back(4);
list.push_back(5);
auto iter = list.begin();
while (iter != list.end())
{
if (*iter == 4)
{
iter = list.erase(iter);
std::cout << *iter << "\n";
list.insert(iter, 3);
// list.insert(iter, 3);
// std::cout << "n: " << *(++iter) << "\n";
// iter = list.erase(++iter);
}
iter++;
}
for (int i : list)
{
std::cout << i << " ";
}
std::cout << std::endl;
}
int main() {
std::cout << "hello" << std::endl;
iter_test();
return 0;
}

View file

@ -1,32 +0,0 @@
#include <stdio.h>
#include "heap.hpp"
struct Obj {
int a;
int b;
int c;
};
int main() {
GC::Heap::init();
Obj *obj;
for (int i = 0; i < 4; i++) {
obj = static_cast<Obj *>(GC::Heap::alloc(sizeof(Obj)));
obj->a = i * i + 1;
obj->b = i * i + 2;
obj->c = i * i + 3;
}
// heap->force_collect();
auto heap = GC::Heap::debug_the();
heap->collect(COLLECT_ALL);
std::cout << obj->a << ", " << obj->b << ", " << obj->c << std::endl;
//delete heap;
GC::Heap::dispose();
return 0;
}

View file

@ -1,250 +0,0 @@
#include <iostream>
#include <list>
#include "heap.hpp"
using GC::Chunk;
void alloc_test();
void add_to_free_list(Chunk *chunk);
void merge_free_list(Chunk *chunk, bool do_merge);
void do_merge_list();
void print_free_list();
std::list<Chunk *> m_free_list;
int main()
{
alloc_test();
// std::list<int> test;
// test.push_back(1);
// test.push_back(2);
// test.push_back(3);
// test.push_back(4);
// test.push_back(5);
// auto iter = test.begin();
// std::cout << "First? " << *(iter++) << "\n";
// std::cout << "Second? " << *(iter--) << "\n";
// std::cout << "First? " << *iter << std::endl;
// auto i = test.begin();
// while (i != test.end())
// {
// std::cout << *i << " ";
// ++i;
// }
// if (i == test.end())
// std::cout << "great success!";
// std::cout << std::endl;
return 0;
}
void alloc_test()
{
auto tmp = static_cast<uintptr_t *>(__builtin_frame_address(0));
auto c1 = new Chunk((size_t)(8), tmp);
auto c2 = new Chunk((size_t)(4), c1->m_start + (size_t)(8));
auto c3 = new Chunk((size_t)(16), c2->m_start + (size_t)(4));
auto c4 = new Chunk((size_t)(4), c3->m_start + (size_t)(16));
auto c5 = new Chunk((size_t)(32), c4->m_start + (size_t)(4));
// std::cout << "test: " << (uintptr_t *)(tmp + (size_t)(2)) << std::endl;
std::cout << "tmp: " << tmp << "\ntmp: " << (tmp + (size_t)(28)) << std::endl;
// add_to_free_list(c1);
// add_to_free_list(c2);
// add_to_free_list(c3);
// add_to_free_list(c4);
// add_to_free_list(c5);
merge_free_list(c1, false);
merge_free_list(c2, false);
merge_free_list(c3, false);
merge_free_list(c4, false);
merge_free_list(c5, false);
std::cout << "----- BEFORE MERGE ----------------------";
// print_free_list();
do_merge_list();
std::cout << "----- AFTER MERGE -----------------------";
// print_free_list();
}
void add_to_free_list(Chunk *chunk)
{
Chunk *curr;
auto iter = m_free_list.begin();
uintptr_t *prev_start = nullptr;
uintptr_t *prev_end = nullptr;
if (m_free_list.size() == 0)
{
m_free_list.push_back(chunk);
return;
}
while (iter != m_free_list.end())
{
curr = *iter;
// If the curr chunk is aligned before param
if (curr->m_start + curr->m_size == chunk->m_start)
{
Chunk *merged = new Chunk(
curr->m_size + chunk->m_size,
curr->m_start);
iter = m_free_list.erase(iter);
m_free_list.insert(iter, merged);
return;
}
// If the curr chunk is aligned after param
if (chunk->m_start + chunk->m_size == curr->m_start)
{
Chunk *merged = new Chunk(
curr->m_size + chunk->m_size,
chunk->m_start);
iter = m_free_list.erase(iter);
m_free_list.insert(iter, merged);
return;
}
// If the first chunk starts after param
if (prev_start == nullptr && curr->m_start > chunk->m_start)
{
m_free_list.insert(iter, chunk);
return;
}
if (prev_end < chunk->m_start && (chunk->m_start + chunk->m_size) < curr->m_start)
{
m_free_list.insert(iter, chunk);
return;
}
prev_start = curr->m_start;
prev_end = prev_start + curr->m_size;
iter++;
}
// This is only reachable if the chunk is at the end
m_free_list.push_back(chunk);
}
void merge_free_list(Chunk *chunk, bool do_merge)
{
auto i = m_free_list.begin();
uintptr_t *prev_start = nullptr, *prev_end;
bool chunk_inserted = false;
while (i != m_free_list.end())
{
// if chunk is left-aligned
if ((*i)->m_start + (*i)->m_size == chunk->m_start)
{
m_free_list.insert(++i, chunk);
chunk_inserted = true;
break;
}
// if chunk is right-aligned
if (chunk->m_start + chunk->m_size == (*i)->m_start)
{
m_free_list.insert(i, chunk);
chunk_inserted = true;
break;
}
// is new first
if (prev_start == nullptr && (*i)->m_start > chunk->m_start)
{
m_free_list.insert(i, chunk);
chunk_inserted = true;
break;
}
// if between chunks
if (prev_end < chunk->m_start && (chunk->m_start + chunk->m_size) < (*i)->m_start)
{
m_free_list.insert(i, chunk);
chunk_inserted = true;
break;
}
prev_start = (*i)->m_start;
prev_end = (*i)->m_start + (*i)->m_size;
i++;
}
// is new last
if (!chunk_inserted && i == m_free_list.end())
m_free_list.push_back(chunk);
if (do_merge)
do_merge_list();
}
void do_merge_list()
{
std::cout << "DO MERGE" << std::endl;
auto i = m_free_list.begin();
Chunk *prev = *(i++), *curr;
print_free_list();
while (i != m_free_list.end())
{
curr = *i;
if ((prev->m_start + prev->m_size) == curr->m_start)
{
Chunk *merged = new Chunk(
prev->m_size + curr->m_size,
prev->m_start
);
// replace current and previous with merged
i = m_free_list.erase(i);
i = m_free_list.erase(--i);
m_free_list.insert(i, merged);
prev = merged;
}
else
{
prev = curr;
i++;
}
print_free_list();
}
print_free_list();
}
void print_free_list()
{
std::cout << "free-list count: " << m_free_list.size() << "\n";
auto iter = m_free_list.begin();
size_t cnt = 1;
while (iter != m_free_list.end())
{
std::cout << "C" << cnt << ":\n\tstart: " << (*iter)->m_start
<< "\n\tsize: " << (*iter)->m_size << "\n";
iter++;
cnt++;
}
std::cout << std::endl;
}

View file

@ -1,44 +0,0 @@
#include <iostream>
#include <stdio.h>
using namespace std;
// broken :(
// [event_source(native)]
class ESource {
public:
__event void TestEvent(int eValue);
};
// [event_receiver(native)]
class EReceiver {
public:
void Handler1(int eValue) {
cout << "Handler1 with: " << eValue << endl;
}
void Handler2(int eValue) {
cout << "Handler2 with: " << eValue << endl;
}
void hookEvent(ESource *eSource) {
__hook(&ESource::TestEvent, eSource, &EReceiver::Handler1);
__hook(&ESource::TestEvent, eSource, &EReceiver::Handler2);
}
void unhookEvent(ESource *eSource) {
__unhook(&ESource::TestEvent, eSource, &EReceiver::Handler1);
__unhook(&ESource::TestEvent, eSource, &EReceiver::Handler2);
}
};
int main() {
ESource src;
EReceiver rcv;
rcv.hookEvent(&src);
__raise src.TestEvent(12);
rcv.unhookEvent(&src);
return 0;
}

View file

@ -1,94 +0,0 @@
#include <cstring>
#include <iostream>
#include "heap.hpp"
GC::Heap& singleton_test();
void init_gc(GC::Heap& heap);
void frame_test(GC::Heap& heap);
int main() {
std::cout << "in main" << std::endl;
GC::Heap &heap = singleton_test();
init_gc(heap);
frame_test(heap);
heap.dispose();
return 0;
}
/**
* This test is supposed to determine if the singleton pattern
* implementation is working correctly. This test passes if the
* first and second call prints the same memory address.
*
* Result: pass
*
* @return Pointer to the Heap singleton instance
*/
GC::Heap& singleton_test() {
std::cout << "TESTING SINGLETON INSTANCES" << std::endl;
std::cout << "===========================" << std::endl;
std::cout << "Call 1:\t" << &GC::Heap::the() << std::endl; // First call which initializes the singleton instance
GC::Heap &heap = GC::Heap::the(); // Second call which should return the initialized instance
std::cout << "Call 2:\t" << &heap << std::endl;
std::cout << "===========================" << std::endl;
return heap;
}
/**
* This test calls Heap::init() which saves the stack-frame
* address from the calling function (this function).
* Heap::init() is supposed to be called at the absolute
* start of the program to save the address of the
* topmost stack frame. This test doesn't do anything
* but prepares for the next test(s).
*
* @param heap The Heap pointer to the singleton instance.
*
*/
void init_gc(GC::Heap& heap){
std::cout << "\n\n INITIALIZING THE HEAP" << std::endl;
std::cout << "===========================" << std::endl;
heap.init();
heap.set_profiler(true);
std::cout << "===========================" << std::endl;
}
/**
* This function tests the functionality of the intrinsic
* function `__builtin_frame_address` which returns the
* address of the corresponding level of stack frame.
* When given a param of 0, it returns the current stack frame.
* When given a param of 1, it returns the previous stack
* frame, and so on.
*
* This test passes on two conditions:
* 1) if the address of the current frame is smaller than
* the address of the previous frame (assumed).
* 2) if the previous frame has the same address as the one
* saved in the Heap instance after running Heap::init().
*
* Result: pass
*
* @param heap The Heap instance
*/
void frame_test(GC::Heap& heap) {
std::cout << "\n\n TESTING FRAME ADDRESSES" << std::endl;
std::cout << "===========================" << std::endl;
#pragma clang diagnostic ignored "-Wframe-address" // clang++ directive to ignore warnings about __b_f_a
auto curr_frame = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0)); // addr of curr stack frame
std::cout << "Current stack frame:\t" << curr_frame << std::endl;
#pragma clang diagnostic ignored "-Wframe-address"
auto prev_frame = reinterpret_cast<uintptr_t *>(__builtin_frame_address(1)); // addr of prev stack frame
std::cout << "Previous stack frame:\t" << prev_frame << std::endl;
heap.check_init(); // prints the saved absolute top of the stack
// auto alloced = heap->alloc(sizeof(unsigned long));
std::cout << "===========================" << std::endl;
}

View file

@ -1,77 +0,0 @@
#include <ctime>
#include <fstream>
#include <iostream>
#include <string>
#include <time.h>
#include <unistd.h>
void time_string(char *buffer);
void print_log_file(const std::string TESTS_PATH);
void readlink_test();
void null_test();
int main()
{
// char time_buffer[31];
// time_string(time_buffer);
// const std::string TESTS_PATH = "/home/virre/dev/systemF/org/language/src/GC/tests/";
// print_log_file(TESTS_PATH);
// readlink_test();
null_test();
return 0;
}
void time_string(char *const buffer)
{
std::time_t tt = std::time(NULL);
std::tm *ptm = std::localtime(&tt);
std::strftime(buffer, 31, "/logs/log_%a_%H_%M_%S.txt", ptm);
std::cout << buffer << std::endl;
}
void print_log_file(const std::string TESTS_PATH)
{
std::string path = TESTS_PATH + "/testlog.txt";
std::ofstream testF(path);
testF << "hellow york";
testF.close();
}
void readlink_test()
{
char buffer[1024];
ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer)-1);
if (len == -1)
{
std::cout << "readlink error" << std::endl;
return;
}
buffer[len] = '\0';
std::cout << "readlink:\n" << "'''" << buffer << "'''"; // << std::endl;
auto path = std::string(buffer);
std::cout << path << "\nlen: " << path.size() << "\ncap:" << path.capacity();
size_t last_slash = path.find_last_of('/');
std::string folder = path.substr(0, last_slash);
std::cout << "\n" << folder;
std::string log_path = folder + "/log_file_bla.txt";
std::cout << "\n" << log_path << std::endl;
}
void null_test() {
int *p = nullptr;
std::cout << "P: " << nullptr << std::endl;
}

View file

@ -1,95 +0,0 @@
#include <vector>
#include "player.hpp"
#include "heap.hpp"
#define X_LENGTH 1000
#define Y_LENGTH 500
#define MAX_PLAYERS 100
/*
* Description:
* This class is designed to test the Garbage Collector with a mock game,
* that consists of several live objects in the form of players, that in
* turn consists partially of Point objects.
*
* Goal:
* to find out if all the objects are allocated successfully
* and to see if they are reachable from the stack, i.e. they can get marked.
*
* Result:
* all objects gets allocated, but only Game object gets marked.
*/
class Game {
private:
std::vector<Player*> *players;
//std::vector<Player> *players;
Point *dimensions;
public:
Game() {
dimensions->x = X_LENGTH;
dimensions->y = Y_LENGTH;
}
void init() {
players = static_cast<std::vector<Player*>*>(GC::Heap::alloc(sizeof(Player*) * MAX_PLAYERS));
//players = static_cast<std::vector<Player>*>(GC::Heap::alloc(sizeof(Player) * MAX_PLAYERS));
dimensions = static_cast<Point*>(GC::Heap::alloc(sizeof(Point)));
dimensions->x = X_LENGTH;
dimensions->y = Y_LENGTH;
}
void add_player(Player *p) {
players->push_back(p);
}
Player* create_player(string *s, Point *pos, Point *size, Point *dir) {
Player *p = static_cast<Player*>(GC::Heap::alloc(sizeof(Player)));
/*
Cannot allocate by new, since it the allocates outside of "out" heap. That also lead so us having to
define an alternative constructor, that's actually a method. Since our "alloc" does not call the constructor
of the object
*/
p->init(s, pos, size, dir);
return p;
}
void create_players(int nr) {
for (int i = 0; i < nr; i++) {
std::string *str = static_cast<std::string*>(GC::Heap::alloc(sizeof(std::string)));
Point *pos = static_cast<Point*>(GC::Heap::alloc(sizeof(Point)));
Point *size = static_cast<Point*>(GC::Heap::alloc(sizeof(Point)));
Point *dir = static_cast<Point*>(GC::Heap::alloc(sizeof(Point)));
Player *p = create_player(str, pos, size, dir);
add_player(p);
}
}
};
int main() {
GC::Heap::init();
GC::Heap *gc = GC::Heap::debug_the();
gc->check_init();
Game *game = static_cast<Game*>(gc->alloc(sizeof(Game)));
game->init();
game->create_players(2);
std::cout << "Player size: " << sizeof(Player) << std::endl;
std::cout << "Game size: " << sizeof(Game) << std::endl;
std::cout << "Point size: " << sizeof(Point) << std::endl;
gc->collect(GC::MARK);
gc->print_contents();
return 0;
}

View file

@ -1,107 +0,0 @@
#include <chrono>
#include <iostream>
#include "heap.hpp"
using std::cout, std::endl;
struct Node {
int id;
Node *child;
};
Node *create_chain(int depth) {
cout << "entering create_chain";
std::vector<Node*> nodes;
if (depth > 0) {
Node *last_node = static_cast<Node *>(GC::Heap::alloc(sizeof(Node)));
last_node->id = depth;
last_node->child = nullptr;
nodes.push_back(last_node);
for (size_t i = 0; i < depth; i++) {
Node *node = static_cast<Node *>(GC::Heap::alloc(sizeof(Node)));
node->id = depth-i;
node->child = nodes[i];
nodes.push_back(node);
}
cout << "\nexiting create_chain" << endl;
return nodes[depth];
}
else
return 0;
}
void create_array(size_t size) {
int *arr = static_cast<int *>(GC::Heap::alloc(sizeof(int) * size));
}
void detach_pointer(long **ptr) {
cout << "entering detach_pointer";
long *dummy_ptr = nullptr;
*ptr = dummy_ptr;
cout << "\nexiting detach_pointer" << endl;
}
Node *test_chain(int depth, bool detach) {
cout << "entering test_chain";
auto stack_start = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
Node *node_chain = create_chain(depth);
if (detach)
node_chain->child = nullptr;
cout << "\nexiting test_chain" << endl;
return node_chain;
}
void test_some_types() {
cout << "entering test_some_types" << endl;
auto stack_start = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
std::cout << "Stack start from test_some_types:\t" << stack_start << std::endl;
long *l = static_cast<long *>(GC::Heap::alloc(sizeof(long)));
std::cout << "l points to:\t\t" << l << std::endl;
detach_pointer(&l);
std::cout << "l points to:\t\t" << l << std::endl;
// Some more dummy values of different sizes, to test stack pointer alignment
int *i = static_cast<int *>(GC::Heap::alloc(sizeof(int)));
char *c = static_cast<char *>(GC::Heap::alloc(sizeof(int)));
short *s = static_cast<short *>(GC::Heap::alloc(sizeof(short)));
cout << "exiting test_some_types" << endl;
}
int main() {
cout << "entering main" << endl;
using namespace std::literals;
auto start = std::chrono::high_resolution_clock::now();
//std::cout << "Value of start: " << start.time_since_epoch().count() << std::endl;
GC::Heap::init();
GC::Heap &gc = GC::Heap::the();
gc.set_profiler(true);
GC::Profiler::set_log_options(GC::FunctionCalls);
gc.check_init();
auto stack_start = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
Node *root1 = static_cast<Node *>(gc.alloc(sizeof(Node)));
Node *root2 = static_cast<Node *>(gc.alloc(sizeof(Node)));
root1 = test_chain(58000, false);
root2 = test_chain(58000, false);
gc.collect(GC::COLLECT_ALL);
auto end = std::chrono::high_resolution_clock::now();
//std::cout << "Value of end: " << end.time_since_epoch().count() << std::endl;
gc.print_summary();
gc.dispose();
std::cout
<< "Execution time: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count() << ""
<< (end - start) / 1ms << "ms ≈ "
<< (end - start) / 1s << "s.\n";
return 0;
}

View file

@ -1,74 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include "heap.hpp"
#define allocNode static_cast<Node *>(GC::Heap::alloc(sizeof(Node)))
using std::cout, std::endl;
struct Node // sizeof(Node) = 16
{
int value;
Node *next {nullptr};
};
Node *create_list(size_t length)
{
Node *head = allocNode;
head->value = 0;
Node *prev = head;
for (size_t i = 1; i < length; i++)
{
Node *next = allocNode;
next->value = i;
prev->next = next;
prev = next;
}
return head;
}
void print_list(Node* head)
{
cout << "\nPrinting list...\n";
while (head != nullptr)
{
cout << head->value << " ";
head = head->next;
}
cout << endl;
}
void clear_list(Node *head)
{
while (head != nullptr)
{
Node *tmp = head->next;
head->next = nullptr;
head = tmp;
}
}
void run_list_test()
{
Node *list = create_list(10);
print_list(list);
}
int main()
{
GC::Heap::init();
GC::Heap &heap = GC::Heap::the();
heap.set_profiler(true);
GC::Profiler::set_log_options(GC::FunctionCalls);
for (int i = 0; i < 10; i++)
run_list_test();
GC::Heap::dispose();
return 0;
}

View file

@ -1,16 +0,0 @@
#include <stdio.h>
#include "heap.hpp"
struct Obj {
int a;
int b;
int c;
};
int main() {
return 0;
}

View file

@ -1,51 +0,0 @@
#include <string>
using std::string;
class Point {
public:
int x, y;
Point() {}
Point(int _x, int _y) : x(_x), y(_y) {}
};
class Player {
private:
string *name;
Point *position;
Point *size;
Point *direction;
public:
Player() {}
/* Player(string n, Point pos, Point s, Point dir)
: name(n), position(pos.x, pos.y), size(s.x, s.y), direction(dir.x, dir.y)
{} */
void move() {
position->x += direction->x;
position->y += direction->y;
}
void set_speed(int dx, int dy) {
direction->x = dx;
direction->y = dy;
}
// This is probably neccessary to initialize an object with our GC
// Since allocation and construction cannot be done at the same time
void init(string *n, Point *pos, Point *s, Point *dir) {
name = n;
position = pos;
size = s;
direction = dir;
}
};

View file

@ -1,76 +0,0 @@
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
/*
* Stack.cpp
* - Tests stack scanning and stack pointers
*
* Goal: Find the values of the following variables
* and their position on the stack
* - unsigned long a
* - unsigned long b
* - unsigned long global_1
* - unsigned long global_2
*
* Result: Passed
*/
std::vector<uintptr_t *> iv;
void collect() {
std::cout << "in collect" << std::endl;
uintptr_t *stack_start = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
// denna orsakar segfault om man ger __b_f_a ett värde större än 2
// uintptr_t *stack_end = reinterpret_cast<uintptr_t *>(__builtin_frame_address(100));
std::cout << "SP1:\t" << stack_start << "\nSP2:\t" << (stack_start - 1*sizeof(int)) << std::endl;
std::cout << "SP-:\t" << --stack_start << std::endl;
const uintptr_t *stack_end = (stack_start + 30*sizeof(int));
int vars_found = 0;
while (stack_start < stack_end) {
if (std::find(iv.begin(), iv.end(), stack_start) != iv.end()) {
vars_found++;
std::cout << "Found " << *(reinterpret_cast<unsigned long *>(stack_start)) << " at " << stack_start << std::endl;
}
// std::cout << "SP address:\t\t" << stack_start << "\nSP value:\t\t" << *(reinterpret_cast<unsigned long *>(stack_start)) << std::endl;
stack_start++;
}
if (vars_found == 0) {
std::cout << "Found nothing" << std::endl;
}
}
int add(unsigned long a, unsigned long b) {
iv.push_back(reinterpret_cast<uintptr_t *>(&a));
iv.push_back(reinterpret_cast<uintptr_t *>(&b));
std::cout << "'a':\t" << &a << "\n'b':\t" << &b << std::endl;
collect();
return a + b;
}
int main() {
unsigned long global_1 = 16;
unsigned long global_2 = 32;
iv.push_back(&global_1);
iv.push_back(&global_2);
std::cout << "'g1':\t" << &global_1 << "\n'g2':\t" << &global_2 << std::endl;
add(3,2);
return 0;
}

View file

@ -1,51 +0,0 @@
#include <cstring>
#include <iostream>
void dummy1();
void dummy2();
int main() {
uintptr_t *prev1 = reinterpret_cast<uintptr_t *>(__builtin_frame_address(0));
uintptr_t *prev2 = static_cast<uintptr_t *>(__builtin_frame_address(0));
std::cout << "reinterpret:\t" << prev1 << "\nstatic:\t\t" << prev2 << std::endl;
std::cout << "Start:\t\t" << prev1 << std::endl;
#pragma clang diagnostic ignored "-Wframe-address"
uintptr_t *tmp = reinterpret_cast<uintptr_t *>(__builtin_frame_address(1));
std::cout << "Frame 1:\t" << tmp << "\t\tDiff:\t" << std::hex << "0x"<< tmp - prev1 << std::endl;
prev1 = tmp;
#pragma clang diagnostic ignored "-Wframe-address"
tmp = reinterpret_cast<uintptr_t *>(__builtin_frame_address(2));
std::cout << "Frame 2:\t" << tmp << "\tDiff:\t" << std::hex << "0x" << tmp - prev1 << std::endl;
prev1 = tmp;
// arg > 2 for __builtin_frame_address() results in segfault
// #pragma clang diagnostic ignored "-Wframe-address"
// tmp = reinterpret_cast<uintptr_t *>(__builtin_frame_address(3));
// std::cout << "Frame 3:\t" << tmp << "\tDiff:\t" << std::hex << "0x" << prev1 - tmp << std::endl;
dummy1();
return 0;
}
void dummy1() {
std::cout << "D1 SFrame:\t" << __builtin_frame_address(0);
#pragma clang diagnostic ignored "-Wframe-address"
std::cout << "\t\tPrev:\t" << __builtin_frame_address(1) << std::endl;
std::cout << "D1 RA:\t\t" << std::hex << __builtin_return_address(0) << std::endl;
dummy2();
}
void dummy2() {
std::cout << "Frame:\t\t" << __builtin_frame_address(0);
#pragma clang diagnostic ignored "-Wframe-address"
std::cout << "\t\tPrev:\t" << __builtin_frame_address(1) << std::endl;
void *ra = __builtin_return_address(0);
std::cout << "D2 RA:\t\t" << std::hex << ra << std::endl;
// gives same value as pure 'ra'
// std::cout << "D2 ERA:\t\t" << std::hex << __builtin_extract_return_addr(ra) << std::endl;
}

View file

@ -1,41 +0,0 @@
#include <iostream>
#include "heap.hpp"
using namespace std;
struct Node {
int value;
Node *left;
Node *right;
};
int getValue();
Node *createNode();
void insert();
int main() {
GC::Heap::init();
Node *node = static_cast<Node *>(GC::Heap::alloc(sizeof(Node)));
return 0;
}
int getValue() {
cout << "Enter a value to insert: ";
int value;
cin >> value;
return value;
}
Node *createNode() {
Node *node = static_cast<Node *>(GC::Heap::alloc(sizeof(Node)));
node->value = getValue();
return node;
}
void insert(Node *root) {
Node *node = createNode();
Node *curr = root;
while (curr)
}

View file

@ -1,96 +0,0 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "cheap.h"
typedef struct object
{
int x, y, z;
double velocity;
} Object;
void test_init()
{
printf("----- IN TEST_INIT ----------------------------\n");
cheap_init();
printf("----- EXIT TEST_INIT --------------------------\n");
}
/* Uncomment ONLY if run with DEBUG defined in cheap.h */
cheap_t *test_the()
{
printf("----- IN TEST_THE -----------------------------\n");
cheap_t *fst_heap = cheap_the();
printf("Heap 1:\t%p\n", fst_heap->obj);
cheap_t *snd_heap = cheap_the();
printf("Heap 2:\t%p\n", snd_heap->obj);
printf("----- EXIT TEST_THE ---------------------------\n");
free(snd_heap);
return fst_heap;
}
void test_profiler(cheap_t *heap)
{
printf("----- IN TEST_PROFILER ------------------------\n");
cheap_set_profiler(heap, false);
cheap_set_profiler(heap, true);
cheap_profiler_log_options(heap, FuncCallsOnly);
printf("----- EXIT TEST_PROFILER ----------------------\n");
}
Object *test_alloc()
{
printf("----- IN TEST_ALLOC ---------------------------\n");
Object *o;
o = (Object *)(cheap_alloc(sizeof(Object)));
o->x = 3;
o->y = 4;
o->z = 5;
o->velocity = 1.0f;
printf("----- EXIT TEST_ALLOC -------------------------\n");
return o;
}
void test_dispose()
{
printf("----- IN TEST_DISPOSE -------------------------\n");
cheap_dispose();
printf("----- EXIT TEST_DISPOSE -----------------------\n");
}
int main()
{
test_init();
/* Uncomment ONLY if run with DEBUG defined in cheap.h */
cheap_t *heap = test_the();
test_profiler(heap);
Object *o = test_alloc();
printf("Object size: %lu\n", sizeof(Object));
printf("Object:\n\tx: %d\n\ty: %d\n\tz: %d\n\tvel: %f\n", o->x, o->y, o->z, o->velocity);
test_dispose();
/* Sefault I don't understand, don't uncomment */
// free(heap);
// free(o);
return 0;
}

View file

@ -1,45 +0,0 @@
#include <stdbool.h>
#include <stdio.h>
#include "cheap.h"
typedef struct node {
int id;
struct node *child;
} Node;
// Global variables make the test less complex
Node *HEAD = NULL;
Node *CURRENT = NULL;
// Creates a linked list of length depth. Global head "HEAD" is updated.
void *create_linked_list(int depth) {
HEAD = (Node*)(cheap_alloc(sizeof(Node)));
HEAD->id = 0;
// Purposely omitting adding a child to "last_node", since its the last node
for (int i = 1; i < depth - 1; i++) {
insert_first(i);
}
}
void *insert_first(int node_id) {
Node *new_head;
new_head = (Node*)(cheap_alloc(sizeof(Node)));
new_head->id = node_id;
new_head->child = HEAD;
HEAD = new_head;
}
void test_linked_list(int list_length){
cheap_init();
cheap_t *heap = cheap_the();
cheap_set_profiler(heap, true);
create_linked_list(list_length);
cheap_dispose();
free(heap);
}
int main (int argc, char **argv) {
test_linked_list(30);
}

View file

@ -1,11 +0,0 @@
# Garbage collection
## Project
Deliver to samuel
## GC TODO:
- PR till master
## Tests TODO
- Write complex datastructures for tests with larger programs
- Testa `__builtin_frame_address` mer specifikt för att se om första stack framen skannas

View file

@ -138,7 +138,8 @@ main' opts s =
log monomorphized
printToErr "\n -- Compiler --"
generatedCode <- fromErr $ generateCode monomorphized (gc opts)
-- generatedCode <- fromErr $ generateCode monomorphized (gc opts)
generatedCode <- fromErr $ generateCode monomorphized False
check <- doesPathExist "output"
when check (removeDirectoryRecursive "output")
@ -148,7 +149,8 @@ main' opts s =
writeFile "output/llvm.ll" generatedCode
debugDotViz
compile generatedCode (gc opts)
-- compile generatedCode (gc opts)
compile generatedCode False
printToErr "Compilation done!"
printToErr "\n-- Program output --"
print =<< spawnWait "./output/hello_world"