Context.cpp 5.7 KB
Newer Older
1
// SPDX-License-Identifier: LGPL-3.0-or-later
2
// Copyright © 2020-2022 Florian Schmaus
Florian Schmaus's avatar
Florian Schmaus committed
3
4
#include "Context.hpp"

5
6
7
#include <sys/mman.h>

#include <cerrno>
8
#include <cstdint>
Florian Schmaus's avatar
Florian Schmaus committed
9
10
#include <ostream>

Florian Schmaus's avatar
Florian Schmaus committed
11
12
#include "stats/Worker.hpp"

Florian Schmaus's avatar
Florian Schmaus committed
13
14
thread_local Context* Context::currentContext;

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
auto Context::calcTos(char* context) -> void* {
	auto contextBase = (uintptr_t)context;
	uintptr_t contextTop = contextBase + CONTEXT_SIZE;
	// Align the Top-of-Stack (tos) to 16 byte.
	uintptr_t tos = contextTop & ~0xf;
	uintptr_t tosOffset = tos - contextBase;
	return context + tosOffset;
}
auto Context::calcTos() -> void* { return calcTos(context); }

auto Context::calcGuardPage(char* context) -> void* {
	if constexpr (!emper::STACK_GUARD_PAGE) return nullptr;

	auto contextBase = (uintptr_t)context;
	uintptr_t guardPageBase = em ::roundUp(contextBase, PAGE_SIZE);
	uintptr_t guardPageOffset = guardPageBase - contextBase;
	return context + guardPageOffset;
}
auto Context::calcGuardPage() -> void* { return calcGuardPage(context); }

35
36
37
38
39
40
41
42
43
44
45
46
auto Context::inGuardPage(void* mem) -> bool {
	if constexpr (!emper::STACK_GUARD_PAGE) return false;

	const auto _guardPage = (uintptr_t)this->guardPage;
	const auto _mem = (uintptr_t)mem;
	return _mem >= _guardPage && _mem < _guardPage + PAGE_SIZE;
}
auto Context::inCurrentGuardPage(void* mem) -> bool {
	if (!currentContext) return false;
	return currentContext->inGuardPage(mem);
}

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
auto Context::calcBos(char* context) -> void* {
	auto contextBase = (uintptr_t)context;
	uintptr_t bosOffset = 0;
	if constexpr (emper::STACK_GUARD_PAGE) {
		uintptr_t guardPageBase = em ::roundUp(contextBase, PAGE_SIZE);
		uintptr_t guardPageTop = guardPageBase + PAGE_SIZE;
		bosOffset = guardPageTop - contextBase;
	}
	return context + bosOffset;
}
auto Context::calcBos() -> void* { return calcBos(context); }

// cppcheck-suppress noExplicitConstructor selfInitialization
Context::Context(func_t mainFunction)
		: tos(calcTos()),
			guardPage(calcGuardPage()),
			bos(calcBos()),
			usableStackSize((char*)tos - (char*)bos),
			mainFunction(std::move(std::move(mainFunction))) {
	//		valgrindStackId = VALGRIND_STACK_REGISTER(context, context + CONTEXT_SI);

	if constexpr (emper::STACK_GUARD_PAGE) {
		int res = mprotect(guardPage, PAGE_SIZE, PROT_NONE);
		if (res) DIE_MSG_ERRNO("Installaing guard page at " << guardPage << " failed");
	}

	maybeMarkStack();

	setEmptyHook();

77
78
79
80
81
	// TOS is 16-byte aligned, however we need the initial
	// savedStackpointer to be 8-byte aligned because will later jmp to
	// it. We also save a pointer to very first function this context is
	// going to execute a this initial stackpointer value.
	alphaFunctionIpLocation = savedStackpointer = (uintptr_t*)tos - 1;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

	void** alphaSavedIp = reinterpret_cast<void**>(savedStackpointer);
	void (*f)() = &kickoff;
	assert(f != nullptr);
	*(alphaSavedIp) = reinterpret_cast<void*>(f);
}

Context::~Context() {
	//		VALGRIND_STACK_DEREGISTER(valgrindStackId);
	if constexpr (emper::STACK_GUARD_PAGE) {
		int res = mprotect(guardPage, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
		if (res) DIE_MSG_ERRNO("Uninstallaing guard page at " << guardPage << " failed");
	}
}

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
void Context::printTo(std::ostream& strm, bool withPtr) const {
	// clang-format off
	strm << "Context [";
	if (withPtr) {
		strm << "ptr=" << this << ", ";
	}
	strm << "tos=" << tos;
	strm << ", bos=" << &context
			 << ", saved-sp=" << savedStackpointer
			 << ", *saved-sp=" << *static_cast<void**>(savedStackpointer);
	if (savedStackpointer != alphaFunctionIpLocation) {
	strm << ", alpha-fun-loc=" << alphaFunctionIpLocation
		   << ", *alpha-func-loc=" << *static_cast<const void* const*>(alphaFunctionIpLocation);
	}
	strm << "]";
	// clang-format off
}

Florian Schmaus's avatar
Florian Schmaus committed
115
auto operator<<(std::ostream& strm, const Context& context) -> std::ostream& {
116
117
118
119
120
121
	context.printTo(strm, false);
	return strm;
}

auto operator<<=(std::ostream& strm, const Context& context) -> std::ostream& {
	context.printTo(strm, true);
Florian Schmaus's avatar
Florian Schmaus committed
122
123
	return strm;
}
124

125
126
127
128
129
130
131
132
133
void Context::unmap(void* sp) const {
	constexpr size_t PAGE_SIZE_MASK = PAGE_SIZE - 1;
	const auto spBase = (uintptr_t)sp;
	const uintptr_t spBasePage = spBase & ~PAGE_SIZE_MASK;
	const auto bosBase = (uintptr_t)bos;
	const uintptr_t bosBasePage = (bosBase + PAGE_SIZE_MASK) & ~PAGE_SIZE_MASK;
	const size_t length = spBasePage - bosBasePage;
	const uintptr_t bosBasePageOffset = bosBasePage - bosBase;
	auto* const addr = (char*)bos + bosBasePageOffset;
134
135
136
137
138
139
140
141
	const int advice = []{
		if constexpr (emper::CONTINUATION_STEALING_MADVISE_STACK == emper::ContinuationStealingMadviseStack::dontneed) {
			return MADV_DONTNEED;
		} else {
			return MADV_FREE;
		}
	}();

142
	LOGD("madvise() addr=" << addr << ", length=" << length << ", advice=" << advice);
143

144
	errno = madvise(addr, length, advice);
145
146
147
	if (errno) {
		DIE_MSG_ERRNO("Unmapping unused stack space failed");
	}
148
}
149
150

void Context::maybeMarkStack() {
Florian Schmaus's avatar
Florian Schmaus committed
151
	if constexpr (!emper::DEBUG && !emper::STATS_STACK_USAGE_ENABLED) return;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

	// Write the stack full of 0xcc bytes, which just happen to be
	// the 'int3' instruction, which will trigger a breakpoint
	// when executed.
	void* res = memset(bos, 0xcc, usableStackSize);
	if (!res) DIE;

	// Mark the last valid 16 bytes just below the top of stack.
	res = memset(((uintptr_t*)tos) - 1, 0xab, sizeof(uintptr_t));
	if (!res) DIE;

	// Mark the eventually existing unused top stack bytes
	res = memset(tos, 0xee, &context[CONTEXT_SIZE] - (char*)tos);
	if (!res) DIE;
}
Florian Schmaus's avatar
Florian Schmaus committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180

void Context::recordStackUsage() {
	constexpr uintptr_t stackMarker = 0xcccccccccccccccc;

	size_t bosOffset;
	for (bosOffset = 0; bosOffset < usableStackSize; bosOffset += sizeof(stackMarker)) {
		auto* stackWord = (uintptr_t*)((char*)bos + bosOffset);
		if (*stackWord != stackMarker) break;
	}

	size_t stackUsage = usableStackSize - bosOffset;

	emper::stats::worker->recordStackUsage(stackUsage);
}