1#include <Common/checkStackSize.h>
2#include <Common/Exception.h>
3#include <ext/scope_guard.h>
4#include <pthread.h>
5#include <cstdint>
6#include <sstream>
7
8#if defined(__FreeBSD__)
9# include <pthread_np.h>
10#endif
11
12
13namespace DB
14{
15 namespace ErrorCodes
16 {
17 extern const int CANNOT_PTHREAD_ATTR;
18 extern const int LOGICAL_ERROR;
19 extern const int TOO_DEEP_RECURSION;
20 }
21}
22
23static thread_local void * stack_address = nullptr;
24static thread_local size_t max_stack_size = 0;
25
26void checkStackSize()
27{
28 using namespace DB;
29
30 if (!stack_address)
31 {
32#if defined(OS_DARWIN)
33 // pthread_get_stacksize_np() returns a value too low for the main thread on
34 // OSX 10.9, http://mail.openjdk.java.net/pipermail/hotspot-dev/2013-October/011369.html
35 //
36 // Multiple workarounds possible, adopt the one made by https://github.com/robovm/robovm/issues/274
37 // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html
38 // Stack size for the main thread is 8MB on OSX excluding the guard page size.
39 pthread_t thread = pthread_self();
40 max_stack_size = pthread_main_np() ? (8 * 1024 * 1024) : pthread_get_stacksize_np(thread);
41
42 // stack_address points to the start of the stack, not the end how it's returned by pthread_get_stackaddr_np
43 stack_address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(pthread_get_stackaddr_np(thread)) - max_stack_size);
44#else
45 pthread_attr_t attr;
46# if defined(__FreeBSD__)
47 pthread_attr_init(&attr);
48 if (0 != pthread_attr_get_np(pthread_self(), &attr))
49 throwFromErrno("Cannot pthread_attr_get_np", ErrorCodes::CANNOT_PTHREAD_ATTR);
50# else
51 if (0 != pthread_getattr_np(pthread_self(), &attr))
52 throwFromErrno("Cannot pthread_getattr_np", ErrorCodes::CANNOT_PTHREAD_ATTR);
53# endif
54
55 SCOPE_EXIT({ pthread_attr_destroy(&attr); });
56
57 if (0 != pthread_attr_getstack(&attr, &stack_address, &max_stack_size))
58 throwFromErrno("Cannot pthread_getattr_np", ErrorCodes::CANNOT_PTHREAD_ATTR);
59#endif // OS_DARWIN
60 }
61
62 const void * frame_address = __builtin_frame_address(0);
63 uintptr_t int_frame_address = reinterpret_cast<uintptr_t>(frame_address);
64 uintptr_t int_stack_address = reinterpret_cast<uintptr_t>(stack_address);
65
66 /// We assume that stack grows towards lower addresses. And that it starts to grow from the end of a chunk of memory of max_stack_size.
67 if (int_frame_address > int_stack_address + max_stack_size)
68 throw Exception("Logical error: frame address is greater than stack begin address", ErrorCodes::LOGICAL_ERROR);
69
70 size_t stack_size = int_stack_address + max_stack_size - int_frame_address;
71
72 /// Just check if we have already eat more than a half of stack size. It's a bit overkill (a half of stack size is wasted).
73 /// It's safe to assume that overflow in multiplying by two cannot occur.
74 if (stack_size * 2 > max_stack_size)
75 {
76 std::stringstream message;
77 message << "Stack size too large"
78 << ". Stack address: " << stack_address
79 << ", frame address: " << frame_address
80 << ", stack size: " << stack_size
81 << ", maximum stack size: " << max_stack_size;
82 throw Exception(message.str(), ErrorCodes::TOO_DEEP_RECURSION);
83 }
84}
85