99// please note that tRPC source code is licensed under the Apache 2.0 License,
1010// A copy of the Apache 2.0 License is included in this file.
1111//
12+ // tRPC is licensed under the Apache 2.0 License, and includes source codes from
13+ // the following components:
14+ // 1. flare
15+ // Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
16+ // flare is licensed under the BSD 3-Clause License.
1217//
1318
1419#include " trpc/runtime/threadmodel/fiber/detail/stack_allocator_impl.h"
2227
2328#include " trpc/runtime/threadmodel/fiber/detail/assembly.h"
2429#include " trpc/util/check.h"
30+ #include " trpc/util/deferred.h"
2531#include " trpc/util/internal/never_destroyed.h"
2632#include " trpc/util/likely.h"
2733
@@ -31,13 +37,123 @@ static bool fiber_use_mmap = true;
3137static uint32_t fiber_stack_size = 131072 ;
3238static bool fiber_stack_enable_guard_page = true ;
3339static uint32_t max_fiber_num_by_mmap = 30 * 1024 ;
40+ static bool enable_gdb_debug = false ;
41+
42+ const uint32_t kPageSize = getpagesize();
43+
44+ // The following source codes are from flare.
45+ // Copied and modified from
46+ // https://github.com/Tencent/flare/blob/master/flare/fiber/detail/stack_allocator.cc
47+
48+ // All stacks (whether system stack or user stack) are registered here. This is
49+ // necessary for our GDB plugin to find all the stacks.
50+ //
51+ // Only _actual_ stack allocation / deallocation needs to touch this. For
52+ // allocations / deallocations covered by our object pool, they're irrelevant
53+ // here.
54+ //
55+ // Registration / deregistration can be slow. But that's okay as it's already
56+ // slow enough to _actually_ creating / destroying stacks. These operations
57+ // incur heavy VMA operations.
58+ struct StackRegistry {
59+ // Listed as public as they're our "public" interfaces to GDB plugin.
60+ //
61+ // Code in this TU should use methods below instead of touching these fields.
62+ void ** stacks = nullptr ; // Leaked on exit. Doesn't matter.
63+ std::size_t used = 0 ;
64+ std::size_t capacity = 0 ;
65+
66+ // Register a newly-allocated stack.
67+ //
68+ // `ptr` should point to stack bottom (i.e. one byte past the stack region).
69+ // That's where our fiber control block (GDB plugin need it) resides.
70+ void RegisterStack (void * ptr) {
71+ std::scoped_lock _ (lock_); // It's slow, so be it.
72+ ++used;
73+ auto slot = UnsafeFindSlotOf (nullptr );
74+ if (slot) {
75+ *slot = ptr;
76+ return ;
77+ }
78+
79+ UnsafeResizeRegistry ();
80+ *UnsafeFindSlotOf (nullptr ) = ptr; // Must succeed this time.
81+ }
82+
83+ // Deregister a going-to-be-freed stack. `ptr` points to stack bottom.
84+ void DeregisterStack (void * ptr) {
85+ std::scoped_lock _ (lock_);
86+
87+ trpc::ScopedDeferred __ ([&] {
88+ // If `stacks` is too large we should consider shrinking it.
89+ if (capacity > 1024 && capacity / 2 > used) {
90+ UnsafeShrinkRegistry ();
91+ }
92+ });
93+
94+ --used;
95+ if (auto p = UnsafeFindSlotOf (ptr)) {
96+ *p = nullptr ;
97+ return ;
98+ }
99+ TRPC_UNREACHABLE ();
100+ }
101+
102+ private:
103+ void ** UnsafeFindSlotOf (void * ptr) {
104+ for (std::size_t i = 0 ; i != capacity; ++i) {
105+ if (stacks[i] == ptr) {
106+ return &stacks[i];
107+ }
108+ }
109+ return nullptr ;
110+ }
111+
112+ void UnsafeShrinkRegistry () {
113+ auto new_capacity = capacity / 2 ;
114+ TRPC_CHECK (new_capacity);
115+ auto new_stacks = new void *[new_capacity];
116+ std::size_t copied = 0 ;
117+
118+ memset (new_stacks, 0 , new_capacity * sizeof (void *));
119+ for (std::size_t i = 0 ; i != capacity; ++i) {
120+ if (stacks[i]) {
121+ new_stacks[copied++] = stacks[i];
122+ }
123+ }
124+
125+ TRPC_CHECK_EQ (copied, used);
126+ TRPC_CHECK_LE (copied, new_capacity);
127+ capacity = new_capacity;
128+ delete[] std::exchange (stacks, new_stacks);
129+ }
130+
131+ void UnsafeResizeRegistry () {
132+ if (capacity == 0 ) { // We haven't been initialized yet.
133+ capacity = 8 ;
134+ stacks = new void *[capacity];
135+ memset (stacks, 0 , sizeof (void *) * capacity);
136+ } else {
137+ auto new_capacity = capacity * 2 ;
138+ auto new_stacks = new void *[new_capacity];
139+ memset (new_stacks, 0 , new_capacity * sizeof (void *));
140+ memcpy (new_stacks, stacks, capacity * sizeof (void *));
141+ capacity = new_capacity;
142+ delete[] std::exchange (stacks, new_stacks);
143+ }
144+ }
145+
146+ private:
147+ std::mutex lock_;
148+ } stack_registry; // Using global variable here. This makes looking up this
149+ // variable easy in GDB plugin.
150+
151+ // End of source codes that are from flare.
34152
35153void SetFiberPoolCreateWay (bool use_mmap) {
36154 fiber_use_mmap = use_mmap;
37155}
38156
39- const uint32_t kPageSize = getpagesize();
40-
41157void SetFiberStackSize (uint32_t stack_size) {
42158 bool ok = (stack_size >= kPageSize ) && ((stack_size & (kPageSize - 1 )) == 0 );
43159 if (!ok) {
@@ -58,6 +174,10 @@ void SetFiberPoolNumByMmap(uint32_t fiber_pool_num_by_mmap) {
58174 max_fiber_num_by_mmap = fiber_pool_num_by_mmap;
59175}
60176
177+ void SetEnableGdbDebug (bool flag) {
178+ enable_gdb_debug = flag;
179+ }
180+
61181// We always align stack top to 1M boundary. This helps our GDB plugin to find
62182// fiber stacks.
63183constexpr auto kStackTopAlignment = 1 * 1024 * 1024 ;
@@ -141,10 +261,23 @@ void* AlignedMmap() {
141261 }
142262 auto stack = reinterpret_cast <char *>(p) + GetBias ();
143263 InitializeFiberStackMagic (stack + fiber_stack_size - kPageSize );
264+
265+ if (enable_gdb_debug) {
266+ auto stack_bottom = stack + fiber_stack_size;
267+ // Register the stack.
268+ stack_registry.RegisterStack (stack_bottom);
269+ }
270+
144271 return reinterpret_cast <void *>(stack);
145272}
146273
147274void AlignedMunmap (void * ptr) {
275+ if (enable_gdb_debug) {
276+ // Remove the stack from our registry.
277+ auto stack_bottom = reinterpret_cast <char *>(ptr) + fiber_stack_size;
278+ stack_registry.DeregisterStack (stack_bottom);
279+ }
280+
148281 TRPC_PCHECK (munmap (reinterpret_cast <char *>(ptr) - GetBias (), GetAllocationsize ()) == 0 );
149282}
150283
@@ -155,10 +288,23 @@ void* AlignedMalloc() {
155288 }
156289 auto stack = reinterpret_cast <char *>(p);
157290 InitializeFiberStackMagic (stack + fiber_stack_size - kPageSize );
291+
292+ if (enable_gdb_debug) {
293+ auto stack_bottom = stack + fiber_stack_size;
294+ // Register the stack.
295+ stack_registry.RegisterStack (stack_bottom);
296+ }
297+
158298 return reinterpret_cast <void *>(stack);
159299}
160300
161301void AlignedFree (void * aligned_mem) {
302+ if (enable_gdb_debug) {
303+ // Remove the stack from our registry.
304+ auto stack_bottom = reinterpret_cast <char *>(aligned_mem) + fiber_stack_size;
305+ stack_registry.DeregisterStack (stack_bottom);
306+ }
307+
162308 return free (aligned_mem);
163309}
164310
0 commit comments