getpc.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
  2. // Copyright (c) 2005, Google Inc.
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. // ---
  31. // Author: Craig Silverstein
  32. //
  33. // This is an internal header file used by profiler.cc. It defines
  34. // the single (inline) function GetPC. GetPC is used in a signal
  35. // handler to figure out the instruction that was being executed when
  36. // the signal-handler was triggered.
  37. //
  38. // To get this, we use the ucontext_t argument to the signal-handler
  39. // callback, which holds the full context of what was going on when
  40. // the signal triggered. How to get from a ucontext_t to a Program
  41. // Counter is OS-dependent.
  42. #ifndef BASE_GETPC_H_
  43. #define BASE_GETPC_H_
  44. #include "config.h"
  45. // On many linux systems, we may need _GNU_SOURCE to get access to
  46. // the defined constants that define the register we want to see (eg
  47. // REG_EIP). Note this #define must come first!
  48. #define _GNU_SOURCE 1
  49. // If #define _GNU_SOURCE causes problems, this might work instead.
  50. // It will cause problems for FreeBSD though!, because it turns off
  51. // the needed __BSD_VISIBLE.
  52. //#define _XOPEN_SOURCE 500
  53. #include <string.h> // for memcmp
  54. #if defined(HAVE_SYS_UCONTEXT_H)
  55. #include <sys/ucontext.h>
  56. #elif defined(HAVE_UCONTEXT_H)
  57. #include <ucontext.h> // for ucontext_t (and also mcontext_t)
  58. #elif defined(HAVE_CYGWIN_SIGNAL_H)
  59. #include <cygwin/signal.h>
  60. typedef ucontext ucontext_t;
  61. #endif
  62. // Take the example where function Foo() calls function Bar(). For
  63. // many architectures, Bar() is responsible for setting up and tearing
  64. // down its own stack frame. In that case, it's possible for the
  65. // interrupt to happen when execution is in Bar(), but the stack frame
  66. // is not properly set up (either before it's done being set up, or
  67. // after it's been torn down but before Bar() returns). In those
  68. // cases, the stack trace cannot see the caller function anymore.
  69. //
  70. // GetPC can try to identify this situation, on architectures where it
  71. // might occur, and unwind the current function call in that case to
  72. // avoid false edges in the profile graph (that is, edges that appear
  73. // to show a call skipping over a function). To do this, we hard-code
  74. // in the asm instructions we might see when setting up or tearing
  75. // down a stack frame.
  76. //
  77. // This is difficult to get right: the instructions depend on the
  78. // processor, the compiler ABI, and even the optimization level. This
  79. // is a best effort patch -- if we fail to detect such a situation, or
  80. // mess up the PC, nothing happens; the returned PC is not used for
  81. // any further processing.
  82. struct CallUnrollInfo {
  83. // Offset from (e)ip register where this instruction sequence
  84. // should be matched. Interpreted as bytes. Offset 0 is the next
  85. // instruction to execute. Be extra careful with negative offsets in
  86. // architectures of variable instruction length (like x86) - it is
  87. // not that easy as taking an offset to step one instruction back!
  88. int pc_offset;
  89. // The actual instruction bytes. Feel free to make it larger if you
  90. // need a longer sequence.
  91. unsigned char ins[16];
  92. // How many bytes to match from ins array?
  93. int ins_size;
  94. // The offset from the stack pointer (e)sp where to look for the
  95. // call return address. Interpreted as bytes.
  96. int return_sp_offset;
  97. };
  98. // The dereferences needed to get the PC from a struct ucontext were
  99. // determined at configure time, and stored in the macro
  100. // PC_FROM_UCONTEXT in config.h. The only thing we need to do here,
  101. // then, is to do the magic call-unrolling for systems that support it.
  102. // -- Special case 1: linux x86, for which we have CallUnrollInfo
  103. #if defined(__linux) && defined(__i386) && defined(__GNUC__)
  104. static const CallUnrollInfo callunrollinfo[] = {
  105. // Entry to a function: push %ebp; mov %esp,%ebp
  106. // Top-of-stack contains the caller IP.
  107. { 0,
  108. {0x55, 0x89, 0xe5}, 3,
  109. 0
  110. },
  111. // Entry to a function, second instruction: push %ebp; mov %esp,%ebp
  112. // Top-of-stack contains the old frame, caller IP is +4.
  113. { -1,
  114. {0x55, 0x89, 0xe5}, 3,
  115. 4
  116. },
  117. // Return from a function: RET.
  118. // Top-of-stack contains the caller IP.
  119. { 0,
  120. {0xc3}, 1,
  121. 0
  122. }
  123. };
  124. inline void* GetPC(const ucontext_t& signal_ucontext) {
  125. // See comment above struct CallUnrollInfo. Only try instruction
  126. // flow matching if both eip and esp looks reasonable.
  127. const int eip = signal_ucontext.uc_mcontext.gregs[REG_EIP];
  128. const int esp = signal_ucontext.uc_mcontext.gregs[REG_ESP];
  129. if ((eip & 0xffff0000) != 0 && (~eip & 0xffff0000) != 0 &&
  130. (esp & 0xffff0000) != 0) {
  131. char* eip_char = reinterpret_cast<char*>(eip);
  132. for (int i = 0; i < sizeof(callunrollinfo)/sizeof(*callunrollinfo); ++i) {
  133. if (!memcmp(eip_char + callunrollinfo[i].pc_offset,
  134. callunrollinfo[i].ins, callunrollinfo[i].ins_size)) {
  135. // We have a match.
  136. void **retaddr = (void**)(esp + callunrollinfo[i].return_sp_offset);
  137. return *retaddr;
  138. }
  139. }
  140. }
  141. return (void*)eip;
  142. }
  143. // Special case #2: Windows, which has to do something totally different.
  144. #elif defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__MINGW32__)
  145. // If this is ever implemented, probably the way to do it is to have
  146. // profiler.cc use a high-precision timer via timeSetEvent:
  147. // http://msdn2.microsoft.com/en-us/library/ms712713.aspx
  148. // We'd use it in mode TIME_CALLBACK_FUNCTION/TIME_PERIODIC.
  149. // The callback function would be something like prof_handler, but
  150. // alas the arguments are different: no ucontext_t! I don't know
  151. // how we'd get the PC (using StackWalk64?)
  152. // http://msdn2.microsoft.com/en-us/library/ms680650.aspx
  153. #include "base/logging.h" // for RAW_LOG
  154. #ifndef HAVE_CYGWIN_SIGNAL_H
  155. typedef int ucontext_t;
  156. #endif
  157. inline void* GetPC(const struct ucontext_t& signal_ucontext) {
  158. RAW_LOG(ERROR, "GetPC is not yet implemented on Windows\n");
  159. return NULL;
  160. }
  161. // Normal cases. If this doesn't compile, it's probably because
  162. // PC_FROM_UCONTEXT is the empty string. You need to figure out
  163. // the right value for your system, and add it to the list in
  164. // configure.ac (or set it manually in your config.h).
  165. #else
  166. inline void* GetPC(const ucontext_t& signal_ucontext) {
  167. return (void*)signal_ucontext.PC_FROM_UCONTEXT; // defined in config.h
  168. }
  169. #endif
  170. #endif // BASE_GETPC_H_