1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
/**
* Copyright (C) 2015 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/platform/basic.h"
#include "mongo/platform/stack_locator.h"
#include "mongo/util/assert_util.h"
namespace mongo {
StackLocator::StackLocator() {
// Please see
//
// http://stackoverflow.com/questions/1740888/determining-stack-space-with-visual-studio/1747499#1747499
//
// for notes on the following arcana.
// TODO: _WIN32_WINNT >= 0x0602 (windows 8 / 2012 server) may be
// able to use GetCurrentThreadStackLimits
// Put something on the stack, convieniently the variable we are
// going to write into, and ask the VM system for information
// about the memory region it inhabits, which is the committed
// part of the stack.
MEMORY_BASIC_INFORMATION committedMbi = {};
invariant(VirtualQuery(&committedMbi, &committedMbi, sizeof(committedMbi)) != 0);
invariant(committedMbi.State == MEM_COMMIT);
// Now committedMbi.AllocationBase points to the reserved stack
// memory base address (the real bottom of the stack), and
// committedMbi.BaseAddress points to base address of the
// committed region, and committedMbi.RegionSize is the size of
// the commit region. Since the stack grows downward, the top of
// the stack is at the base address for the commit region plus the
// region size.
_begin = static_cast<char*>(committedMbi.BaseAddress) + committedMbi.RegionSize;
// Now, we skip down to the bottom, where the uncommitted memory
// is, and get its size. So, we ask for the region at the
// allocation base (the real bottom of the stack), after which
// uncommitedMbi will have a BaseAddress and a RegionSize that
// describes the uncommited area. The memory should have the
// RESERVE state set.
MEMORY_BASIC_INFORMATION uncommittedMbi = {};
invariant(VirtualQuery(committedMbi.AllocationBase, &uncommittedMbi, sizeof(uncommittedMbi)) !=
0);
invariant(committedMbi.AllocationBase == uncommittedMbi.AllocationBase);
invariant(uncommittedMbi.RegionSize > 0);
// If the stack was created with CreateThread with dwStackSize
// non-zero and without the STACK_SIZE_PARAM_IS_A_RESERVATION
// flag, then the whole AllocationBase is our stack base, and
// there is no guard page.
if (uncommittedMbi.State == MEM_COMMIT) {
// NOTE: Originally, it seemed to make sense that what you would get back
// here would be the same information as in committedMbi. After all, the whole
// stack is committed. So, querying AllocationBase should give you back
// the same region.
//
// Bizzarely, that doesn't seem to be the case. Even though
// it has the same protections, state, etc., VirtualQuery seems
// to consider the stack that has been used distinct from the part
// that hasn't.
//
// invariant(uncommittedMbi.BaseAddress == committedMbi.BaseAddress);
// invariant(uncommittedMbi.RegionSize == committedMbi.RegionSize);
//
_end = static_cast<char*>(committedMbi.AllocationBase);
return;
}
invariant(uncommittedMbi.State == MEM_RESERVE);
if (kDebugBuild) {
// Locate the guard page, which sits bewteen the uncommitted
// region (which we know is not empty!), and the committed
// region. We can count the guard page as usable stack space,
// but it is good to find it so we can validate that we walked
// the stack correctly.
// The end of the guard page is right after the uncommitted
// area. Form a pointer into the guard page by skipping over the
// committed region.
const auto guard =
static_cast<char*>(uncommittedMbi.BaseAddress) + uncommittedMbi.RegionSize;
// With that pointer in hand, get the info about the guard
// region. We really only care about its size here (and we
// validate that it has the right bits).
MEMORY_BASIC_INFORMATION guardMbi = {};
invariant(VirtualQuery(guard, &guardMbi, sizeof(guardMbi)) != 0);
invariant(committedMbi.AllocationBase == guardMbi.AllocationBase);
invariant((guardMbi.Protect & PAGE_GUARD) != 0);
invariant(guardMbi.RegionSize > 0);
}
// The end of our stack is the allocation base for the whole stack.
_end = static_cast<char*>(committedMbi.AllocationBase);
}
} // namespace mongo
|