Changes imported from Abseil "staging" branch:
- b00fb48c537d494ab775bc6701acfb8e100d8b88 Add a test utility for measuring stack consumption. by Derek Mauro <dmauro@google.com> GitOrigin-RevId: b00fb48c537d494ab775bc6701acfb8e100d8b88 Change-Id: I00466a6d6c14911c12d34f62a4144925748e3f61
This commit is contained in:
		
							parent
							
								
									0fa86cac40
								
							
						
					
					
						commit
						bf7fc9986e
					
				
					 4 changed files with 291 additions and 0 deletions
				
			
		|  | @ -17,6 +17,7 @@ | ||||||
| load( | load( | ||||||
|     "//absl:copts.bzl", |     "//absl:copts.bzl", | ||||||
|     "ABSL_DEFAULT_COPTS", |     "ABSL_DEFAULT_COPTS", | ||||||
|  |     "ABSL_TEST_COPTS", | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| package( | package( | ||||||
|  | @ -167,3 +168,28 @@ cc_test( | ||||||
|         "@com_google_googletest//:gtest_main", |         "@com_google_googletest//:gtest_main", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|  | 
 | ||||||
|  | cc_library( | ||||||
|  |     name = "stack_consumption", | ||||||
|  |     testonly = 1, | ||||||
|  |     srcs = ["internal/stack_consumption.cc"], | ||||||
|  |     hdrs = ["internal/stack_consumption.h"], | ||||||
|  |     copts = ABSL_DEFAULT_COPTS, | ||||||
|  |     deps = [ | ||||||
|  |         "//absl/base", | ||||||
|  |         "//absl/base:core_headers", | ||||||
|  |     ], | ||||||
|  |     visibility = ["//visibility:private"], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | cc_test( | ||||||
|  |     name = "stack_consumption_test", | ||||||
|  |     srcs = ["internal/stack_consumption_test.cc"], | ||||||
|  |     copts = ABSL_TEST_COPTS, | ||||||
|  |     deps = [ | ||||||
|  |         ":stack_consumption", | ||||||
|  |         "//absl/base", | ||||||
|  |         "//absl/base:core_headers", | ||||||
|  |         "@com_google_googletest//:gtest_main", | ||||||
|  |     ], | ||||||
|  | ) | ||||||
|  |  | ||||||
							
								
								
									
										172
									
								
								absl/debugging/internal/stack_consumption.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								absl/debugging/internal/stack_consumption.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,172 @@ | ||||||
|  | //
 | ||||||
|  | // Copyright 2018 The Abseil Authors.
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
|  | // you may not use this file except in compliance with the License.
 | ||||||
|  | // You may obtain a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | #include "absl/debugging/internal/stack_consumption.h" | ||||||
|  | 
 | ||||||
|  | #ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION | ||||||
|  | 
 | ||||||
|  | #include <signal.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "absl/base/attributes.h" | ||||||
|  | #include "absl/base/internal/raw_logging.h" | ||||||
|  | 
 | ||||||
|  | namespace absl { | ||||||
|  | namespace debugging_internal { | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | // This code requires that we know the direction in which the stack
 | ||||||
|  | // grows. It is commonly believed that this can be detected by putting
 | ||||||
|  | // a variable on the stack and then passing its address to a function
 | ||||||
|  | // that compares the address of this variable to the address of a
 | ||||||
|  | // variable on the function's own stack. However, this is unspecified
 | ||||||
|  | // behavior in C++: If two pointers p and q of the same type point to
 | ||||||
|  | // different objects that are not members of the same object or
 | ||||||
|  | // elements of the same array or to different functions, or if only
 | ||||||
|  | // one of them is null, the results of p<q, p>q, p<=q, and p>=q are
 | ||||||
|  | // unspecified. Therefore, instead we hardcode the direction of the
 | ||||||
|  | // stack on platforms we know about.
 | ||||||
|  | #if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) | ||||||
|  | constexpr bool kStackGrowsDown = true; | ||||||
|  | #else | ||||||
|  | #error Need to define kStackGrowsDown | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // To measure the stack footprint of some code, we create a signal handler
 | ||||||
|  | // (for SIGUSR2 say) that exercises this code on an alternate stack. This
 | ||||||
|  | // alternate stack is initialized to some known pattern (0x55, 0x55, 0x55,
 | ||||||
|  | // ...). We then self-send this signal, and after the signal handler returns,
 | ||||||
|  | // look at the alternate stack buffer to see what portion has been touched.
 | ||||||
|  | //
 | ||||||
|  | // This trick gives us the the stack footprint of the signal handler.  But the
 | ||||||
|  | // signal handler, even before the code for it is exercised, consumes some
 | ||||||
|  | // stack already. We however only want the stack usage of the code inside the
 | ||||||
|  | // signal handler. To measure this accurately, we install two signal handlers:
 | ||||||
|  | // one that does nothing and just returns, and the user-provided signal
 | ||||||
|  | // handler. The difference between the stack consumption of these two signals
 | ||||||
|  | // handlers should give us the stack foorprint of interest.
 | ||||||
|  | 
 | ||||||
|  | void EmptySignalHandler(int) {} | ||||||
|  | 
 | ||||||
|  | // This is arbitrary value, and could be increase further, at the cost of
 | ||||||
|  | // memset()ting it all to known sentinel value.
 | ||||||
|  | constexpr int kAlternateStackSize = 64 << 10;  // 64KiB
 | ||||||
|  | 
 | ||||||
|  | constexpr int kSafetyMargin = 32; | ||||||
|  | constexpr char kAlternateStackFillValue = 0x55; | ||||||
|  | 
 | ||||||
|  | // These helper functions look at the alternate stack buffer, and figure
 | ||||||
|  | // out what portion of this buffer has been touched - this is the stack
 | ||||||
|  | // consumption of the signal handler running on this alternate stack.
 | ||||||
|  | // This function will return -1 if the alternate stack buffer has not been
 | ||||||
|  | // touched. It will abort the program if the buffer has overflowed or is about
 | ||||||
|  | // to overflow.
 | ||||||
|  | int GetStackConsumption(const void* const altstack) { | ||||||
|  |   const char* begin; | ||||||
|  |   int increment; | ||||||
|  |   if (kStackGrowsDown) { | ||||||
|  |     begin = reinterpret_cast<const char*>(altstack); | ||||||
|  |     increment = 1; | ||||||
|  |   } else { | ||||||
|  |     begin = reinterpret_cast<const char*>(altstack) + kAlternateStackSize - 1; | ||||||
|  |     increment = -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   for (int usage_count = kAlternateStackSize; usage_count > 0; --usage_count) { | ||||||
|  |     if (*begin != kAlternateStackFillValue) { | ||||||
|  |       ABSL_RAW_CHECK(usage_count <= kAlternateStackSize - kSafetyMargin, | ||||||
|  |                      "Buffer has overflowed or is about to overflow"); | ||||||
|  |       return usage_count; | ||||||
|  |     } | ||||||
|  |     begin += increment; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ABSL_RAW_LOG(FATAL, "Unreachable code"); | ||||||
|  |   return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | 
 | ||||||
|  | int GetSignalHandlerStackConsumption(void (*signal_handler)(int)) { | ||||||
|  |   // The alt-signal-stack cannot be heap allocated because there is a
 | ||||||
|  |   // bug in glibc-2.2 where some signal handler setup code looks at the
 | ||||||
|  |   // current stack pointer to figure out what thread is currently running.
 | ||||||
|  |   // Therefore, the alternate stack must be allocated from the main stack
 | ||||||
|  |   // itself.
 | ||||||
|  |   void* altstack = mmap(nullptr, kAlternateStackSize, PROT_READ | PROT_WRITE, | ||||||
|  |                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||||||
|  |   ABSL_RAW_CHECK(altstack != MAP_FAILED, "mmap() failed"); | ||||||
|  | 
 | ||||||
|  |   // Set up the alt-signal-stack (and save the older one).
 | ||||||
|  |   stack_t sigstk; | ||||||
|  |   memset(&sigstk, 0, sizeof(sigstk)); | ||||||
|  |   stack_t old_sigstk; | ||||||
|  |   sigstk.ss_sp = altstack; | ||||||
|  |   sigstk.ss_size = kAlternateStackSize; | ||||||
|  |   sigstk.ss_flags = 0; | ||||||
|  |   ABSL_RAW_CHECK(sigaltstack(&sigstk, &old_sigstk) == 0, | ||||||
|  |                  "sigaltstack() failed"); | ||||||
|  | 
 | ||||||
|  |   // Set up SIGUSR1 and SIGUSR2 signal handlers (and save the older ones).
 | ||||||
|  |   struct sigaction sa; | ||||||
|  |   memset(&sa, 0, sizeof(sa)); | ||||||
|  |   struct sigaction old_sa1, old_sa2; | ||||||
|  |   sigemptyset(&sa.sa_mask); | ||||||
|  |   sa.sa_flags = SA_ONSTACK; | ||||||
|  | 
 | ||||||
|  |   // SIGUSR1 maps to EmptySignalHandler.
 | ||||||
|  |   sa.sa_handler = EmptySignalHandler; | ||||||
|  |   ABSL_RAW_CHECK(sigaction(SIGUSR1, &sa, &old_sa1) == 0, "sigaction() failed"); | ||||||
|  | 
 | ||||||
|  |   // SIGUSR2 maps to signal_handler.
 | ||||||
|  |   sa.sa_handler = signal_handler; | ||||||
|  |   ABSL_RAW_CHECK(sigaction(SIGUSR2, &sa, &old_sa2) == 0, "sigaction() failed"); | ||||||
|  | 
 | ||||||
|  |   // Send SIGUSR1 signal and measure the stack consumption of the empty
 | ||||||
|  |   // signal handler.
 | ||||||
|  |   // The first signal might use more stack space. Run once and ignore the
 | ||||||
|  |   // results to get that out of the way.
 | ||||||
|  |   ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed"); | ||||||
|  | 
 | ||||||
|  |   memset(altstack, kAlternateStackFillValue, kAlternateStackSize); | ||||||
|  |   ABSL_RAW_CHECK(kill(getpid(), SIGUSR1) == 0, "kill() failed"); | ||||||
|  |   int base_stack_consumption = GetStackConsumption(altstack); | ||||||
|  | 
 | ||||||
|  |   // Send SIGUSR2 signal and measure the stack consumption of signal_handler.
 | ||||||
|  |   ABSL_RAW_CHECK(kill(getpid(), SIGUSR2) == 0, "kill() failed"); | ||||||
|  |   int signal_handler_stack_consumption = GetStackConsumption(altstack); | ||||||
|  | 
 | ||||||
|  |   // Now restore the old alt-signal-stack and signal handlers.
 | ||||||
|  |   ABSL_RAW_CHECK(sigaltstack(&old_sigstk, nullptr) == 0, | ||||||
|  |                  "sigaltstack() failed"); | ||||||
|  |   ABSL_RAW_CHECK(sigaction(SIGUSR1, &old_sa1, nullptr) == 0, | ||||||
|  |                  "sigaction() failed"); | ||||||
|  |   ABSL_RAW_CHECK(sigaction(SIGUSR2, &old_sa2, nullptr) == 0, | ||||||
|  |                  "sigaction() failed"); | ||||||
|  | 
 | ||||||
|  |   ABSL_RAW_CHECK(munmap(altstack, kAlternateStackSize) == 0, "munmap() failed"); | ||||||
|  |   if (signal_handler_stack_consumption != -1 && base_stack_consumption != -1) { | ||||||
|  |     return signal_handler_stack_consumption - base_stack_consumption; | ||||||
|  |   } | ||||||
|  |   return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace debugging_internal
 | ||||||
|  | }  // namespace absl
 | ||||||
|  | 
 | ||||||
|  | #endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
 | ||||||
							
								
								
									
										45
									
								
								absl/debugging/internal/stack_consumption.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								absl/debugging/internal/stack_consumption.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | //
 | ||||||
|  | // Copyright 2018 The Abseil Authors.
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
|  | // you may not use this file except in compliance with the License.
 | ||||||
|  | // You may obtain a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | // Helper function for measuring stack consumption of signal handlers.
 | ||||||
|  | 
 | ||||||
|  | #ifndef ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ | ||||||
|  | #define ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_ | ||||||
|  | 
 | ||||||
|  | // The code in this module is not portable.
 | ||||||
|  | // Use this feature test macro to detect its availability.
 | ||||||
|  | #ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION | ||||||
|  | #error ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION cannot be set directly | ||||||
|  | #elif !defined(__APPLE__) && \ | ||||||
|  |     (defined(__i386__) || defined(__x86_64__) || defined(__ppc__)) | ||||||
|  | #define ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION 1 | ||||||
|  | 
 | ||||||
|  | namespace absl { | ||||||
|  | namespace debugging_internal { | ||||||
|  | 
 | ||||||
|  | // Returns the stack consumption in bytes for the code exercised by
 | ||||||
|  | // signal_handler.  To measure stack consumption, signal_handler is registered
 | ||||||
|  | // as a signal handler, so the code that it exercises must be async-signal
 | ||||||
|  | // safe.  The argument of signal_handler is an implementation detail of signal
 | ||||||
|  | // handlers and should ignored by the code for signal_handler.  Use global
 | ||||||
|  | // variables to pass information between your test code and signal_handler.
 | ||||||
|  | int GetSignalHandlerStackConsumption(void (*signal_handler)(int)); | ||||||
|  | 
 | ||||||
|  | }  // namespace debugging_internal
 | ||||||
|  | }  // namespace absl
 | ||||||
|  | 
 | ||||||
|  | #endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
 | ||||||
|  | 
 | ||||||
|  | #endif  // ABSL_DEBUGGING_INTERNAL_STACK_CONSUMPTION_H_
 | ||||||
							
								
								
									
										48
									
								
								absl/debugging/internal/stack_consumption_test.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								absl/debugging/internal/stack_consumption_test.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | //
 | ||||||
|  | // Copyright 2018 The Abseil Authors.
 | ||||||
|  | //
 | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||||
|  | // you may not use this file except in compliance with the License.
 | ||||||
|  | // You may obtain a copy of the License at
 | ||||||
|  | //
 | ||||||
|  | //      http://www.apache.org/licenses/LICENSE-2.0
 | ||||||
|  | //
 | ||||||
|  | // Unless required by applicable law or agreed to in writing, software
 | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||||
|  | // See the License for the specific language governing permissions and
 | ||||||
|  | // limitations under the License.
 | ||||||
|  | 
 | ||||||
|  | #include "absl/debugging/internal/stack_consumption.h" | ||||||
|  | 
 | ||||||
|  | #ifdef ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "gtest/gtest.h" | ||||||
|  | #include "absl/base/internal/raw_logging.h" | ||||||
|  | 
 | ||||||
|  | namespace absl { | ||||||
|  | namespace debugging_internal { | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | static void SimpleSignalHandler(int signo) { | ||||||
|  |   char buf[100]; | ||||||
|  |   memset(buf, 'a', sizeof(buf)); | ||||||
|  | 
 | ||||||
|  |   // Never true, but prevents compiler from optimizing buf out.
 | ||||||
|  |   if (signo == 0) { | ||||||
|  |     ABSL_RAW_LOG(INFO, "%p", static_cast<void*>(buf)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(SignalHandlerStackConsumptionTest, MeasuresStackConsumption) { | ||||||
|  |   // Our handler should consume reasonable number of bytes.
 | ||||||
|  |   EXPECT_GE(GetSignalHandlerStackConsumption(SimpleSignalHandler), 100); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | }  // namespace debugging_internal
 | ||||||
|  | }  // namespace absl
 | ||||||
|  | 
 | ||||||
|  | #endif  // ABSL_INTERNAL_HAVE_DEBUGGING_STACK_CONSUMPTION
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue