青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大熊的口袋

 

[轉載]Stack frame omission (FPO) optimization and consequences when debugging

Part1:

During the course of debugging programs, you’ve probably ran into the term “FPO” once or twice. FPO refers to a specific class of compiler optimizations that, on x86, deal with how the compiler accesses local variables and stack-based arguments.

With a function that uses local variables (and/or stack-based arguments), the compiler needs a mechanism to reference these values on the stack. Typically, this is done in one of two ways:
Access local variables directly from the stack pointer (esp). This is the behavior if FPO optimization is enabled. While this does not require a separate register to track the location of locals and arguments, as is needed if FPO optimization is disabled, it makes the generated code slightly more complicated. In particular, the displacement from esp of locals and arguments actually changes as the function is executed, due to things like function calls or other instructions that modify the stack. As a result, the compiler must keep track of the actual displacement from the current esp value at each location in a function where a stack-based value is referenced. This is typically not a big deal for a compiler to do, but in hand written assembler, this can get a bit tricky.
Dedicate a register to point to a fixed location on the stack relative to local variables and and stack-based arguments, and use this register to access locals and arguments. This is the behavior if FPO optimization is disabled. The convention is to use the ebp register to access locals and stack arguments. Ebp is typically setup such that the first stack argument can be found at [ebp+08], with local variables typically at a negative displacement from ebp.

A typical prologue for a function with FPO optimization disabled might look like this:
push   ebp               ; save away old ebp (nonvolatile)
mov    ebp, esp          ; load ebp with the stack pointer
sub    esp, sizeoflocals ; reserve space for locals
...                      ; rest of function

The main concept is that FPO optimization is disabled, a function will immediately save away ebp (as the first operation touching the stack), and then load ebp with the current stack pointer. This sets up a stack layout like so (relative to ebp):
[ebp-01]   Last byte of the last local variable
[ebp+00]   Old ebp value
[ebp+04]   Return address
[ebp+08]   First argument...

Thereafter, the function will always use ebp to access locals and stack based arguments. (The prologue of the function may vary a bit, especially with functions using a variation __SEH_prolog to setup an initial SEH frame, but the end result is always the same with respect to the stack layout relative to ebp.)

This does (as previously stated) make it so that the ebp register is not available for other uses to the register allocator. However, this performance hit is usually not enough to be a large concern relative to a function compiled with FPO optimization turned on. Furthermore, there are a number of conditions that require a function to use a frame pointer which you may hit anyway:
Any function using SEH must use a frame pointer, as when an exception occurs, there is no way to know the displacement of local variables from the esp value (stack pointer) at exception dispatching (the exception could have happened anywhere, and operations like making function calls or setting up stack arguments for a function call modify the value of esp).
Any function using automatic C++ objects with destructors must use SEH for compiler unwind support. This means that most C++ functions end up with FPO optimization disabled. (It is possible to change the compiler assumptions about SEH exceptions and C++ unwinding, but the default [and recommended setting] is to unwind objects when an SEH exception occurs.)
Any function using _alloca to dynamically allocate memory on the stack must use a frame pointer (and thus have FPO optimization disabled), as the displacement from esp for local variables and arguments can change at runtime and is not known to the compiler at compile time when code is being generated.

Because of these restrictions, many functions you may be writing will already have FPO optimization disabled, without you having explicitly turned it off. However, it is still likely that many of your functions that do not meet the above criteria have FPO optimization enabled, and thus do not use ebp to reference locals and stack arguments.

Now that you have a general idea of just what FPO optimization does, I’ll cover cover why it is to your advantage to turn off FPO optimization globally when debugging certain classes of problems in the second half of this series. (It is actually the case that most shipping Microsoft system code turns off FPO as well, so you can rest assured that a real cost benefit analysis has been done between FPO and non-FPO optimized code, and it is overall better to disable FPO optimization in the general case.)

Update: Pavel Lebedinsky points out that the C++ support for SEH exceptions is disabled by default for new projects in VS2005 (and that it is no longer the recommended setting). For most programs built prior to VS2005 and using the defaults at that time, though, the above statement about C++ destructors causing SEH to be used for a function (and thus requiring the use of a frame pointer) still applies.



Part2:
Last time, I outlined the basics as to just what FPO does, and what it means in terms of generated code when you compile programs with or without FPO enabled. This article builds on the last, and lays out just what the impacts of having FPO enabled (or disabled) are when you end up having to debug a program.

For the purposes of this article, consider the following example program with several do-nothing functions that shuffle stack arguments around and call eachother. (For the purposes of this posting, I have disabled global optimizations and function inlining.)
__declspec(noinline)
void
f3(
   int* c,
   char* b,
   int a
   )
{
   *c = a * 3 + (int)strlen(b);

   __debugbreak();
}

__declspec(noinline)
int
f2(
   char* b,
   int a
   )
{
   int c;

   f3(
      &c,
      b + 1,
      a - 3);

   return c;
}

__declspec(noinline)
int
f1(
   int a,
   char* b
   )
{
   int c;

   c = f2(
      b,
      a + 10);

   c ^= (int)rand();

   return c + 2 * a;
}

int
__cdecl
wmain(
   int ac,
   wchar_t** av
   )
{
   int c;

   c = f1(
      (int)rand(),
      "test");

   printf("%d\n",
      c);

   return 0;
}

If we run the program and break in to the debugger at the hardcoded breakpoint, with symbols loaded, everything is as one might expect:
0:000> k
ChildEBP RetAddr
0012ff3c 010015ef TestApp!f3+0x19
0012ff4c 010015fe TestApp!f2+0x15
0012ff54 0100161b TestApp!f1+0x9
0012ff5c 01001896 TestApp!wmain+0xe
0012ffa0 77573833 TestApp!__tmainCRTStartup+0x10f
0012ffac 7740a9bd kernel32!BaseThreadInitThunk+0xe
0012ffec 00000000 ntdll!_RtlUserThreadStart+0x23

Regardless of whether FPO optimization is turned on or off, since we have symbols loaded, we’ll get a reasonable call stack either way. The story is different, however, if we do not have symbols loaded. Looking at the same program, with FPO optimizations enabled and symbols not loaded, we get somewhat of a mess if we ask for a call stack:
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available.
Following frames may be wrong.
0012ff4c 010015fe TestApp+0x15d8
0012ffa0 77573833 TestApp+0x15fe
0012ffac 7740a9bd kernel32!BaseThreadInitThunk+0xe
0012ffec 00000000 ntdll!_RtlUserThreadStart+0x23

Comparing the two call stacks, we lost three of the call frames entirely in the output. The only reason we got anything slightly reasonable at all is that WinDbg’s stack trace mechanism has some intelligent heuristics to guess the location of call frames in a stack where frame pointers are used.

If we look back to how call stacks are setup with frame pointers (from the previous article), the way a program trying to walk the stack on x86 without symbols works is by treating the stack as a sort of linked list of call frames. Recall that I mentioned the layout of the stack when a frame pointer is used:
[ebp-01]   Last byte of the last local variable
[ebp+00]   Old ebp value
[ebp+04]   Return address
[ebp+08]   First argument...

This means that if we are trying to perform a stack walk without symbols, the way to go is to assume that ebp points to a “structure” that looks something like this:
typedef struct _CALL_FRAME
{
   struct _CALL_FRAME* Next;
   void*               ReturnAddress;
} CALL_FRAME, * PCALL_FRAME;

Note how this corresponds to the stack layout relative to ebp that I described above.

A very simple stack walk function designed to walk frames that are compiled with frame pointer usage might then look like so (using the _AddressOfReturnAddress intrinsic to find “ebp”, assuming that the old ebp is 4 bytes before the address of the return address):
LONG
StackwalkExceptionHandler(
   PEXCEPTION_POINTERS ExceptionPointers
   )
{
   if (ExceptionPointers->ExceptionRecord->ExceptionCode
      == EXCEPTION_ACCESS_VIOLATION)
      return EXCEPTION_EXECUTE_HANDLER;

   return EXCEPTION_CONTINUE_SEARCH;
}

void
stackwalk(
   void* ebp
   )
{
   PCALL_FRAME frame = (PCALL_FRAME)ebp;

   printf("Trying ebp %p\n",
      ebp);

   __try
   {
      for (unsigned i = 0;
          i < 100;
          i++)
      {
         if ((ULONG_PTR)frame & 0x3)
         {
            printf("Misaligned frame\n");
            break;
         }

         printf("#%02lu %p  [@ %p]\n",
            i,
            frame,
            frame->ReturnAddress);

         frame = frame->Next;
      }
   }
   __except(StackwalkExceptionHandler(
      GetExceptionInformation()))
   {
      printf("Caught exception\n");
   }
}

#pragma optimize("y", off)
__declspec(noinline)
void printstack(
   )
{
   void* ebp = (ULONG*)_AddressOfReturnAddress()
     - 1;

   stackwalk(
      ebp);
}
#pragma optimize("", on)

If we recompile the program, disable FPO optimizations, and insert a call to printstack inside the f3 function, the console output is something like so:
Trying ebp 0012FEB0
#00 0012FEB0  [@ 0100185C]
#01 0012FED0  [@ 010018B4]
#02 0012FEF8  [@ 0100190B]
#03 0012FF2C  [@ 01001965]
#04 0012FF5C  [@ 01001E5D]
#05 0012FFA0  [@ 77573833]
#06 0012FFAC  [@ 7740A9BD]
#07 0012FFEC  [@ 00000000]
Caught exception

In other words, without using any symbols, we have successfully performed a stack walk on x86.

However, this all breaks down when a function somewhere in the call stack does not use a frame pointer (i.e. was compiled with FPO optimizations enabled). In this case, the assumption that ebp always points to a CALL_FRAME structure is no longer valid, and the call stack is either cut short or is completely wrong (especially if the function in question repurposed ebp for some other use besides as a frame pointer). Although it is possible to use heuristics to try and guess what is really a call/return address record on the structure, this is really nothing more than an educated guess, and tends to be at least slightly wrong (and typically missing one or more frames entirely).

Now, you might be wondering why you might care about doing stack walk operations without symbols. After all, you have symbols for the Microsoft binaries that your program will be calling (such as kernel32) available from the Microsoft symbol server, and you (presumably) have private symbols corresponding to your own program for use when you are debugging a problem.

Well, the answer to that is that you will end up needing to record stack traces without symbols in the course of normal debugging for a wide variety of problems. The reason for this is that there is a lot of support baked into NTDLL (and NTOSKRNL) to assist in debugging a class of particularly insidious problems: handle leaks (and other problems where the wrong handle value is getting closed somewhere and you need to find out why), memory leaks, and heap corruption.

These (very useful!) debugging features offer options that allow you to configure the system to log a stack trace on each heap allocation, heap free, or each time a handle is opened or closed. Now the way these features work is that they will capture the stack trace in real time as the heap operation or handle operation happens, but instead of trying to break into the debugger to display the results of this output (which is undesirable for a number of reasons), they save a copy of the current stack trace in-memory and then continue execution normally. To display these saved stack traces, the !htrace, !heap -p, and !avrf commands have functionality that locates these saved traces in-memory and prints them out to the debugger for you to inspect.

However, NTDLL/NTOSKRNL needs a way to create these stack traces in the first place, so that it can save them for later inspection. There are a couple of requirements here:
The functionality to capture stack traces must not rely on anything layed above NTDLL or NTOSKRNL. This already means that anything as complicated as downloading and loading symbols via DbgHelp is instantly out of the picture, as those functions are layered far above NTDLL / NTOSKRNL (and indeed, they must make calls into the same functions that would be logging stack traces in the first place in order to find symbols).
The functionality must work when symbols for everything on the call stack are not even available to the local machine. For instance, these pieces of functionality must be deployable on a customer computer without giving that computer access to your private symbols in some fashion. As a result, even if there was a good way to locate symbols where the stack trace is being captured (which there isn’t), you couldn’t even find the symbols if you wanted to.
The functionality must work in kernel mode (for saving handle traces), as handle tracing is partially managed by the kernel itself and not just NTDLL.
The functionality must use a minimum amount of memory to store each stack trace, as operations like heap allocation, heap deallocation, handle creation, and handle closure are extremely frequent operations throughout the lifetime of the process. As a result, options like just saving the entire thread stack for later inspection when symbols are available cannot be used, since that would be prohibitively expensive in terms of memory usage for each saved stack trace.

Given all of these restrictions, the code responsible for saving stack traces needs to operate without symbols, and it must furthermore be able to save stack traces in a very concise manner (without using a great deal of memory for each trace).

As a result, on x86, the stack trace saving code in NTDLL and NTOSKRNL assumes that all functions in the call frame use frame pointers. This is the only realistic option for saving stack traces on x86 without symbols, as there is insufficient information baked into each individual compiled binary to reliably perform stack traces without assuming the use of a frame pointer at each call site. (The 64-bit platforms that Windows supports solve this problem with the use of extensive unwind metadata, as I have covered in a number of past articles.)

So, the functionality exposed by pageheap’s stack trace logging, and handle tracing are how stack traces without symbols end up mattering to you, the developer with symbols for all of your binaries, when you are trying to debug a problem. If you make sure to disable FPO optimization on all of your code, then you’ll be able to use tools like pageheap’s stack tracing on heap operations, UMDH (the user mode heap debugger), and handle tracing to track down heap-related problems and handle-related problems. The best part of these features is that you can even deploy them on a customer site without having to install a full debugger (or run your program under a debugger), only later taking a minidump of your process for examination in the lab. All of them rely on FPO optimizations being disabled (at least on x86), though, so remember to turn FPO optimizations off on your release builds for the increased debuggability of these tough-to-find problems in the field.

posted on 2009-10-29 18:01 大熊的口袋 閱讀(836) 評論(0)  編輯 收藏 引用 所屬分類: cpp

導航

統(tǒng)計

公告

常用鏈接

留言簿(2)

隨筆分類

隨筆檔案

win32 & debug

搜索

積分與排名

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲激情电影中文字幕| 久久综合婷婷| 久久视频在线视频| 91久久在线播放| 久久夜精品va视频免费观看| 国产精品美女主播| 亚洲视频在线二区| 亚洲国产精品成人精品| 美国三级日本三级久久99| 国内一区二区在线视频观看| 久久国产免费| 亚洲乱码国产乱码精品精98午夜| 免费观看久久久4p| 亚洲国产欧洲综合997久久| 久久这里有精品视频| 午夜精品影院在线观看| 亚洲尤物视频网| 国产伦精品一区二区三区高清版 | 亚洲精品韩国| 免费观看成人| 一区二区三区欧美亚洲| 国产综合色产| 在线日韩欧美视频| 亚洲国产成人精品女人久久久 | 国产日韩免费| 久久精品国亚洲| 欧美不卡在线视频| 国产主播精品| 欧美黄色影院| 欧美日韩国产色综合一二三四| 日韩一区二区精品葵司在线| 亚洲午夜精品久久| 午夜老司机精品| 揄拍成人国产精品视频| 亚洲国产精品久久久久秋霞影院| 欧美精彩视频一区二区三区| 亚洲一区欧美激情| 欧美日韩亚洲精品内裤| 欧美日韩专区在线| 欧美一区二区三区在线视频| 亚洲精品三级| 亚洲欧美日本另类| 久久免费少妇高潮久久精品99| 娇妻被交换粗又大又硬视频欧美| 欧美凹凸一区二区三区视频| 欧美日韩亚洲一区三区| 久久精品国产一区二区电影 | 亚洲第一在线视频| 欧美色图天堂网| 免费成人高清视频| 美女国产一区| 小黄鸭精品密入口导航| 你懂的视频一区二区| 国产精品视频yy9299一区| 另类av导航| 国产精品激情| 亚洲国产精品久久久久秋霞影院| 国产精品日日摸夜夜摸av| 亚洲国产成人av| 国产精品日韩精品欧美在线 | 欧美亚洲一区二区三区| 亚洲国产精品久久精品怡红院| 国语自产精品视频在线看抢先版结局| 亚洲调教视频在线观看| 久久蜜桃精品| 欧美主播一区二区三区| 欧美激情中文不卡| 免费成人av| 国产手机视频精品| 一本久久综合亚洲鲁鲁| 亚洲国产精品va在看黑人| 香蕉乱码成人久久天堂爱免费 | 精品69视频一区二区三区| 久久综合综合久久综合| 国产精品系列在线播放| 亚洲专区一区| 欧美精品在线视频| 欧美成人免费小视频| 国产一区二区三区最好精华液 | 国产精品免费观看视频| 最新日韩av| 亚洲精品国产精品乱码不99| 久久国产精品高清| 欧美在线观看视频在线| 国产精品久久久久高潮| 久久免费国产精品1| 久久久噜噜噜| 国产一区视频在线观看免费| 亚洲欧美在线观看| 久久不射电影网| 亚洲电影免费观看高清完整版 | 午夜在线电影亚洲一区| 免费视频久久| 欧美在线视频免费播放| 欧美日韩一二三四五区| 亚洲第一精品在线| 尤物yw午夜国产精品视频| 午夜精品一区二区三区在线播放| 免费不卡在线视频| 免费久久精品视频| 永久555www成人免费| 亚洲精品一区二区在线观看| 欧美在线一二三| 国产精品久久一卡二卡| 一区二区三区**美女毛片| 亚洲免费在线精品一区| 国产精品一区二区在线观看| 亚洲一区二区黄| 久久丁香综合五月国产三级网站| 亚洲精品乱码久久久久久| 你懂的网址国产 欧美| 亚洲黄色有码视频| 一区二区三区高清视频在线观看| 欧美日韩成人在线| 亚洲一区免费看| 久久久久久国产精品mv| 在线国产亚洲欧美| 欧美精品乱人伦久久久久久| 亚洲午夜精品17c| 久久久久综合网| 亚洲精品日本| 国产精品视频福利| 久久久午夜电影| 亚洲精品一品区二品区三品区| 亚洲一区在线看| 黄色一区二区在线| 欧美精品一区二区三区高清aⅴ| 香港久久久电影| 伊人成综合网伊人222| 欧美激情在线免费观看| 午夜视频在线观看一区| 欧美激情1区2区3区| 久久男人资源视频| 国产综合久久久久久| 欧美成人午夜免费视在线看片| 亚洲免费不卡| 久久精品免费看| 9久草视频在线视频精品| 国产女人aaa级久久久级| 免费在线亚洲| 欧美网站在线| 亚洲人成在线观看网站高清| 亚洲网站在线看| 亚洲伦理网站| 欧美日韩国产成人在线免费| 一区免费视频| 欧美gay视频| 男女激情久久| 99ri日韩精品视频| 国产一区二区欧美日韩| 亚洲美女免费精品视频在线观看| 国产欧美一级| 久久国产日韩欧美| 欧美在线观看网址综合| 国产亚洲第一区| 亚洲丰满在线| 久久久www成人免费精品| 99re8这里有精品热视频免费 | 欧美mv日韩mv亚洲| 久久一本综合频道| 亚洲在线观看视频网站| 欧美日韩精品综合| 欧美高清免费| 国产一二三精品| 久久精品视频免费| 午夜影视日本亚洲欧洲精品| 欧美大片一区二区三区| 亚洲一区二区久久| 欧美www视频| 性xx色xx综合久久久xx| 99成人精品| 国产中文一区| 国产嫩草一区二区三区在线观看| 欧美a一区二区| 亚洲欧美中文日韩v在线观看| 久久久综合精品| 久久福利视频导航| 亚久久调教视频| 欧美一区二区国产| 欧美亚洲在线播放| 亚洲精品一区二区三区福利| 欧美激情视频在线播放 | 国产日本欧洲亚洲| 国产欧美日本一区二区三区| 国产精品亚洲综合一区在线观看| 国产精品福利在线| 国产欧美激情| 国产综合久久久久久| 精品二区视频| 亚洲国产精品精华液网站| 亚洲三级电影全部在线观看高清| 亚洲精品久久久久久久久久久久久 | 韩国一区二区在线观看| 伊人春色精品| 亚洲看片免费| 亚洲欧美怡红院| 在线观看成人网| 午夜久久影院| 欧美午夜影院| 国内成人精品2018免费看 |