Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

Fix profilegc implementation using GC itself #40

Merged
merged 2 commits into from
Jan 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 35 additions & 21 deletions src/rt/profilegc.d
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ import core.stdc.stdlib;
import core.stdc.string;

import core.exception : onOutOfMemoryError;
import rt.util.hashtab;

struct Entry { size_t count, size; }

char[] buffer;
Entry[char[]] newCounts;
HashTab!(char[], Entry) newCounts;
char[] logfilename = "profilegc.log";

/****
Expand All @@ -36,24 +37,27 @@ char[] logfilename = "profilegc.log";

extern (C) void profilegc_setlogfilename(char[] name)
{
logfilename = name;
logfilename = name ~ "\0";
}



public void accumulate(char[] file, uint line, char[] funcname, char[] type, size_t sz)
public void accumulate(char[] file, uint line, char[] funcname, char[] type,
size_t sz)
{
if (sz == 0)
return;

char[3 * line.sizeof + 1] buf;
auto buflen = snprintf(buf.ptr, buf.length, "%u".ptr, line);

auto lngth = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen;
if (lngth > buffer.length)
auto ln = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen;
if (ln > buffer.length)
{
// Enlarge buffer[] so it is big enough
auto p = cast(char*)realloc(buffer.ptr, lngth);
assert(buffer.length > 0 || buffer.ptr is null);
auto p = cast(char*)realloc(buffer.ptr, ln + 1);
if (!p)
onOutOfMemoryError();
buffer = p[0 .. lngth];
buffer = p[0 .. ln + 1];
}

// "type funcname file:line"
Expand All @@ -67,14 +71,19 @@ public void accumulate(char[] file, uint line, char[] funcname, char[] type, siz
buffer[type.length + 1 + funcname.length + 1 + file.length] = ':';
buffer[type.length + 1 + funcname.length + 1 + file.length + 1 ..
type.length + 1 + funcname.length + 1 + file.length + 1 + buflen] = buf[0 .. buflen];
buffer[ln] = 0;

if (auto pcount = cast(char[])buffer[0 .. lngth] in newCounts)
if (auto pcount = buffer[0 .. ln] in newCounts)
{ // existing entry
pcount.count++;
pcount.size += sz;
}
else
newCounts[buffer[0..lngth].dup] = Entry(1, sz); // new entry
{
auto key = (cast(char*) malloc(char.sizeof * ln))[0 .. ln];
key[] = buffer[0..ln];
newCounts[key] = Entry(1, sz); // new entry
}
}

// Write report to stderr
Expand All @@ -86,18 +95,27 @@ static ~this()
Entry entry;

// qsort() comparator to sort by count field
extern (C) static int qsort_cmp(in void *r1, in void *r2)
extern (C) static int qsort_cmp(void *r1, void *r2)
{
auto result1 = cast(Result*)r1;
auto result2 = cast(Result*)r2;

ptrdiff_t cmp = result2.entry.size - result1.entry.size;
if (cmp) return cmp < 0 ? -1 : 1;
cmp = result2.entry.count - result1.entry.count;
return cmp < 0 ? -1 : (cmp > 0 ? 1 : 0);
if (cmp) return cmp < 0 ? -1 : 1;

if (result2.name == result1.name)
return 0;
// ascending order for names reads better
return result2.name > result1.name ? -1 : 1;
}
}

Result[] counts = new Result[newCounts.length];
size_t size = newCounts.length;
Result[] counts = (cast(Result*) malloc(size * Result.sizeof))[0 .. size];
scope(exit)
free(counts.ptr);

size_t i;
foreach (name, entry; newCounts)
Expand All @@ -110,13 +128,10 @@ static ~this()
if (counts.length)
{
qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp);

FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename ~
'\0').ptr, "w".ptr);
FILE* fp = logfilename.length == 0 ? stdout : fopen(logfilename.ptr, "w".ptr);
if (fp)
{
fprintf(fp,
"bytes allocated, allocations, type, function, file:line\n".ptr);
fprintf(fp, "bytes allocated, allocations, type, function, file:line\n".ptr);
foreach (ref c; counts)
{
fprintf(fp, "%15llu\t%15llu\t%8.*s\n".ptr,
Expand All @@ -127,7 +142,6 @@ static ~this()
fclose(fp);
}
else
fprintf(stderr, "cannot write profilegc log file '%.*s'".ptr,
logfilename.length, logfilename.ptr);
fprintf(stderr, "cannot write profilegc log file '%.*s'".ptr, logfilename.length, logfilename.ptr);
}
}
160 changes: 160 additions & 0 deletions src/rt/util/array.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/**
* Array container for internal usage.
*
* Copyright: Copyright Martin Nowak 2013.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Martin Nowak
*/
module rt.util.array;

static import common = rt.util.common;
import core.exception;

struct Array(T)
{
void reset()
{
length = 0;
}

size_t length()
{
return _length;
}

void length(size_t nlength)
{
size_t reqsize = T.sizeof * nlength;
if (nlength < _length)
foreach (ref val; _ptr[nlength .. _length]) common.destroy(val);
_ptr = cast(T*)common.xrealloc(_ptr, reqsize);
if (nlength > _length)
foreach (ref val; _ptr[_length .. nlength]) common.initialize(val);
_length = nlength;
}

bool empty()
{
return !length;
}

T* front()
in { assert(!empty); }
body
{
return &_ptr[0];
}

T* back()
in { assert(!empty); }
body
{
return &_ptr[_length - 1];
}

T opIndex(size_t idx)
in { assert(idx < length); }
body
{
return _ptr[idx];
}

void opIndexAssign(T value, size_t idx)
in { assert(idx < length); }
body
{
_ptr[idx] = value;
}

T[] opSlice()
{
return _ptr[0 .. _length];
}

T[] opSlice(size_t a, size_t b)
in { assert(a < b && b <= length); }
body
{
return _ptr[a .. b];
}

void insertBack()(T val)
{
length = length + 1;
*back() = val;
}

void popBack()
{
length = length - 1;
}

void remove(size_t idx)
in { assert(idx < length); }
body
{
for (auto i = idx; i < length - 1; ++i)
_ptr[i] = _ptr[i+1];
popBack();
}

void swap(ref Array other)
{
auto ptr = _ptr;
_ptr = other._ptr;
other._ptr = ptr;
auto len = _length;
_length = other._length;
other._length = len;
}

invariant
{
assert(!_ptr == !_length);
}

private:
T* _ptr;
size_t _length;
}

unittest
{
Array!(int) ary;

assert(ary[] == null);
ary.insertBack(5);
assert(ary[] == [5]);
ary.popBack();
assert(ary[] == null);
ary.insertBack(0);
ary.insertBack(1);
assert(ary[] == [0, 1]);
assert(ary[0 .. 1] == [0]);
assert(ary[1 .. 2] == [1]);
assert(ary[ary.length - 2 .. ary.length] == [0, 1]);
size_t idx;
foreach (val; ary[]) assert(idx++ == val);
foreach (i, val; ary[]) assert(i == val);

ary.insertBack(2);
ary.remove(1);
assert(ary[] == [0, 2]);

assert(!ary.empty);
ary.reset();
assert(ary.empty);
ary.insertBack(0);
assert(!ary.empty);
common.destroy(ary);
assert(ary.empty);

Array!(int) ary2;

ary2.insertBack(0);
assert(ary.empty);
assert(ary2[] == [0]);
ary.swap(ary2);
assert(ary[] == [0]);
assert(ary2.empty);
}
54 changes: 54 additions & 0 deletions src/rt/util/common.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Common code for writing containers.
*
* Copyright: Copyright Martin Nowak 2013.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Martin Nowak
*/
module rt.util.common;

import core.exception;
import core.stdc.string;
import core.stdc.stdlib : malloc, realloc;
public import core.stdc.stdlib : free;

void* xrealloc(void* ptr, size_t sz)
{
if (!sz) { .free(ptr); return null; }
if (auto nptr = .realloc(ptr, sz)) return nptr;
.free(ptr); onOutOfMemoryError();
assert(0);
}

void* xmalloc(size_t sz)
{
if (auto nptr = .malloc(sz))
return nptr;
onOutOfMemoryError();
assert(0);
}

void destroy(T)(ref T t)
{
static if (is(T == struct))
{
t.reset();
}

t = T.init;
}

void initialize(T)(ref T t)
{
static if (is(T == struct))
{
if (auto p = typeid(T).initializer().ptr)
memcpy(&t, p, T.sizeof);
else
memset(&t, 0, T.sizeof);
}
else
{
t = T.init;
}
}
Loading