stacktrace_win32-inl.inc 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // Copyright 2017 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. // Produces a stack trace for Windows. Normally, one could use
  16. // stacktrace_x86-inl.h or stacktrace_x86_64-inl.h -- and indeed, that
  17. // should work for binaries compiled using MSVC in "debug" mode.
  18. // However, in "release" mode, Windows uses frame-pointer
  19. // optimization, which makes getting a stack trace very difficult.
  20. //
  21. // There are several approaches one can take. One is to use Windows
  22. // intrinsics like StackWalk64. These can work, but have restrictions
  23. // on how successful they can be. Another attempt is to write a
  24. // version of stacktrace_x86-inl.h that has heuristic support for
  25. // dealing with FPO, similar to what WinDbg does (see
  26. // http://www.nynaeve.net/?p=97). There are (non-working) examples of
  27. // these approaches, complete with TODOs, in stacktrace_win32-inl.h#1
  28. //
  29. // The solution we've ended up doing is to call the undocumented
  30. // windows function RtlCaptureStackBackTrace, which probably doesn't
  31. // work with FPO but at least is fast, and doesn't require a symbol
  32. // server.
  33. //
  34. // This code is inspired by a patch from David Vitek:
  35. // https://code.google.com/p/google-perftools/issues/detail?id=83
  36. #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
  37. #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_
  38. #include <windows.h> // for GetProcAddress and GetModuleHandle
  39. #include <cassert>
  40. typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
  41. IN ULONG frames_to_skip,
  42. IN ULONG frames_to_capture,
  43. OUT PVOID *backtrace,
  44. OUT PULONG backtrace_hash);
  45. // It is not possible to load RtlCaptureStackBackTrace at static init time in
  46. // UWP. CaptureStackBackTrace is the public version of RtlCaptureStackBackTrace
  47. #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
  48. !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
  49. static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
  50. &::CaptureStackBackTrace;
  51. #else
  52. // Load the function we need at static init time, where we don't have
  53. // to worry about someone else holding the loader's lock.
  54. static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
  55. (RtlCaptureStackBackTrace_Function*)GetProcAddress(
  56. GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
  57. #endif // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
  58. template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
  59. static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
  60. const void*, int* min_dropped_frames) {
  61. USHORT n = 0;
  62. if (!RtlCaptureStackBackTrace_fn || skip_count < 0 || max_depth < 0) {
  63. // can't get a stacktrace with no function/invalid args
  64. } else {
  65. n = RtlCaptureStackBackTrace_fn(static_cast<ULONG>(skip_count) + 2,
  66. static_cast<ULONG>(max_depth), result, 0);
  67. }
  68. if (IS_STACK_FRAMES) {
  69. // No implementation for finding out the stack frame sizes yet.
  70. memset(sizes, 0, sizeof(*sizes) * n);
  71. }
  72. if (min_dropped_frames != nullptr) {
  73. // Not implemented.
  74. *min_dropped_frames = 0;
  75. }
  76. return n;
  77. }
  78. namespace absl {
  79. ABSL_NAMESPACE_BEGIN
  80. namespace debugging_internal {
  81. bool StackTraceWorksForTest() {
  82. return false;
  83. }
  84. } // namespace debugging_internal
  85. ABSL_NAMESPACE_END
  86. } // namespace absl
  87. #endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_WIN32_INL_H_